笔者文笔功力尚浅,若有不妥,请慷慨指出,一定感激涕零node
在面试的时候常常被问到让手写关于链表的代码,下面几个都是我在面试中被问到过的问题。固然我写的不必定是最优解,若是有更好的解决办法欢迎你们指出。git
便于你们观看,我先将题目列出github
你们必定要本身在电脑手敲一遍,最好是在纸上本身手写一遍面试
题目:给定一个链表,删除链表的倒数第 n 个节点,而且返回链表的头结点。算法
1给定一个链表: 1->2->3->4->5, 和 n = 2.
2
3当删除了倒数第二个节点后,链表变为 1->2->3->5.
复制代码
在链表的题目中,有时候一个指针解决不了的问题那么咱们就再加一个指针。通常来讲两个指针就能解决大部分的问题markdown
上面是咱们定义的链表,例如咱们想要删除倒数第N个节点,那么就定义两个指针,一个快指针,一个慢指针。快指针比慢指针快N步,而后快慢指针一块儿向前移动,那么正好快指针走到Null
的时候慢指针所指向的就是咱们要删除的节点。oop
举个例子例如咱们想要删除倒数第二个节点,那么初始化的指针指针指向以下。编码
遍历完之后的指针以下,咱们就能够看到左边的指针指向的就是咱们想要删除的节点spa
部分代码以下3d
1public void deleteNodeForN(Node head, int n){
2
3 Node left = head;
4 Node right = head;
5 int i = 0;
6 while (right!=null && i < n){
7 right = right.getNext();
8 i++;
9 }
10
11 while (right!=null){
12 left = left.getNext();
13 right = right.getNext();
14 }
15 // 遍历完后删除左节点
16 deleteNode(left);
17
18}
复制代码
题目:反转一个单链表。
1输入: 0->1->2->3->4->5->NULL
2输出: 5->4->3->2->1->0->NULL
复制代码
链表中没有什么问题是经过加指针解决不了的,若是有,那么就再加一个指针。
在上面链表删除第N个节点中咱们加了两个指针解决了问题,那么接下来若是要反转一个链表该怎么作呢?两个指针已经不够用了,咱们须要三个指针用来定义当前节点、当前节点的前节点、当前节点的后节点。固然这种方式是既不占用空间,时间也快的一种解法。
仍是咱们定义的一个链表,那么咱们想要的指针效果是什么样呢?接下来咱们用图示一步一步演示怎么用三个指针将链表翻转过来,你们不用看我最后给出的解法答案,能够本身试着看着个人图本身写一遍代码,看能不能写出来。
部分代码展现
1public Node reversalNodeThree(Node head) {
2 if (head == null || head.getNext() == null){
3 return head;
4 }
5
6 Node preNode = null;
7 Node nextNode = null;
8 while (head != null){
9 nextNode = head.getNext();
10 head.setNext(preNode);
11 preNode = head;
12 head = nextNode;
13 }
14 return preNode;
15}
复制代码
递归代码的关键是如何将大问题分解为小问题的规律,而且基于此写出递归公式,而后再推敲终止条件。
在写递归代码的时候咱们的思路千万不要一步一步往里套,套着套着本身就会容易蒙了。其实递归的本质就是分解小任务执行,而咱们正确的思惟方式屏蔽掉递归的细节,假设后面的已经咱们想要的结果,而后只想第一步便可。
咱们就以反转链表为例子,怎么用递归的思想来思考,又怎样把咱们的思考变成代码。
这里0号节点的下一节点不是5号节点,而是咱们灰色背景下的大节点
仍是上面的链表为例,咱们要反转,假设第一个节点随后的全部节点已经反转成功了,那么接下来咱们怎么作呢?相信这里你们都会了吧,至关于两个节点的转换。
1Node(0).getNext().setNext(Node(0));
2Node(0).setNext(null);
复制代码
咱们利用上面的终止条件以及分析出来的代码就能够写出以下的递归反转一个链条的代码了。
1public Node reversalNodeTwo(Node head){
2
3 if (head == null || head.getNext() == null){
4 return head;
5 }
6
7 Node reHead = reversalNodeTwo(head.getNext());
8
9 head.getNext().setNext(head);
10
11 head.setNext(null);
12
13 return reHead;
14
15}
复制代码
题目:将两个有序链表合并为一个新的有序链表并返回。新链表是经过拼接给定的两个链表的全部节点组成的。
1输入:1->2->4, 1->3->4
2输出:1->1->2->3->4->4
复制代码
迭代的方式将两个链表合并是经过指针的方式来解决问题的。加入咱们如今有下面两个链表。咱们会定义两个指针分别指向两个链表的表头,来开始进行一一比较,较小的一方将节点移出来,并将指针向后移动。直至为null。接下来咱们用图片分解每一步。你们能够根据图片的提示本身先编码练习一下,后面附有答案。
部分代码展现
1public Node mergeTwoListTwo(Node nodeOne, Node nodeTwo){
2
3 AboutLinkedList mergeTwoList = new AboutLinkedList();
4 Node headNodeOne = nodeOne;
5 Node headNodeTwo = nodeTwo;
6
7 while (headNodeOne!=null || headNodeTwo!=null){
8
9 if (headNodeOne == null || headNodeOne.getNum() > headNodeTwo.getNum()){
10 mergeTwoList.addNode(headNodeTwo);
11 Node pre = headNodeTwo;
12 headNodeTwo = headNodeTwo.getNext();
13 pre.setNext(null);
14 }else {
15 mergeTwoList.addNode(headNodeOne);
16 Node pre = headNodeOne;
17 headNodeOne = headNodeOne.getNext();
18 pre.setNext(null);
19 }
20 }
21 return mergeTwoList.head.getNext();
22}
复制代码
题目:求链表的中间节点
1输入:0->1->2->3->4
2输出:2
复制代码
通常来讲链表的题咱们能够用指针的话不管是时间仍是空间都是最优的解决办法,其实这里有个小技巧,就是定义两个指针(快慢指针),快指针每次走两个节点,慢指针每次走一个节点。这样当快指针走到最后的时候慢指针正好走到了中间位置。接下来咱们用图更直观的感觉一下指针是如何走的。你们能够按照图中的演示本身写一下代码,而后再看我最后给出的代码。这样会记忆更深入
部分代码展现
1public Node getNodeForCenter(Node head){
2 if (head == null){
3 return null;
4 }else if (head.getNext() == null){
5 return head;
6 }
7 Node slow = head;
8 Node fast = head;
9 while (fast!=null && fast.getNext()!=null){
10 slow = slow.getNext();
11 fast = fast.getNext().getNext();
12 }
13 return slow;
14}
复制代码
最后提醒,你们必定要本身在电脑手敲一遍,最好是在纸上本身手写一遍