文章目录
链表常见面试题
一、 移除链表元素
1. 题目
2.思路
- 1.设置两个并行的结点,一个是cur结点,一个是cur的前驱结点
- 2.由cur遍历结点,判断等于cur,前驱结点存cur后一个结点的地址值,cur后移
- 3.cur不等于,前驱结点移动带cur,cur后移,也就是说,等于要删除的元素,跳过,不等于的保留
3.图解
4.解题步骤
- 1.判断头结点是否为空
- 2.设置prev为头结点,cur为下一个结点(头结点等于val的情况最后考虑)
- 3.遍历链表,直到cur==null为止
- 4.如果cur的值等于val,prev的next域引用cur下一个结点的地址,cur后移一位
- 5.否则,prev移动到cur,cur后移一位
- 6.最后处理头结点,如果头结点刚好等于val,将头结点后移一位。
5.代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {//如果头结点为空,直接返回
return null;
}
ListNode prev = head;
ListNode cur = head.next;
while (cur != null) {
if (cur.val == val) {
prev.next = cur.next;
//cur = cur.next;
} else {
prev = cur;
//cur = cur.next;
}
cur = cur.next;
}
if (head.val == val) {//最后处理头结点
//如果头结点的值等于key,头结点后移
head = head.next;
}
return head;
}
}
作者:翁佳明
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二、反转链表
1.题目
2 图解
3.思路
将头结点断开,然后后面的结点用头插法,依次插到前面,完成链表的反转
4.解题步骤
- 1.先分别判断头结点是否为空,是否只存在一个结点
- 2.设置头节点的下一个结点为cur结点
- 3.断开头节点
- 4.遍历链表,直到cur为空,设置curNext结点,存储cur下一个结点的位置
- 5.让头节点连上cur,头节点移动到cur位置
- 6.将cur移动到记录的curNext结点
5.代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null){//判断头结点是否为空
return null;
}
if (head.next == null){
return head;//再判断是否只有一个结点,返回这个结点
}
ListNode cur = head.next; //cur为头结点的下一个结点
head.next = null;//先让头结点断开,不指向任何结点
while (cur!=null){
ListNode curNext = cur.next;//curNext结点是cur的下一个结点
cur.next = head;//将当前的cur后面连上head结点
head = cur;//头结点移动到cur的位置
cur = curNext;//将cur移动到记录的curNext结点
}
return head;
}
}
三、链表的中间结点
1.题目
2.思路
- 利用快慢指针找到中间结点
- slow走一步,fast走两步
- fast和slow同时出发,fast速度是slow的速度的一倍,路程相等,当fas到达终点,slow到达中间
- fast等于空和fast.next等于空时停下,返回slow位置的结点
3.图解
4.解题步骤
- 1.将fast和slow设立在头结点的位置
- 2.fast刚好走到末尾时,fast.next为空,链表的结点是奇数个,fast如果走出链表,此时fast为空,链表的结点是偶数个,无论哪种情况,slow都是符合要求的中间结点
- 3.fast== null || fast.next==null是循环截止的条件
- 4.fast != null && fast.next !=null是循环运行的条件
- 5.让slow一次走一步,fast一次走两步
- 6.返回slow位置的结点,就是中间结点
5.代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow = slow.next;
}
return slow;
}
}
四、链表中倒数第K个结点
1.题目
2.思路
- 利用快慢指针
- 倒数第k个结点,相当于从倒数第1的结点开始,向前走k-1步
- 只要设置fast和slow两个指针,相差k-1步,同时移动
- 当fast走到末尾 ,slow就是倒数第k个结点
3.图解
4.解题过程
- 判断k的合法性 同时避免空指针异常
- 在头结点是位置设立fast和slow两个指针
- 先让fast走k-1步,同时判断k是否超出链表长度
- 让fast和slow同时移动,直到fast的next为空
- 返回slow位置的结点
5.代码
/*public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(k <= 0 || head==null){//避免空指针异常
return null;
}
ListNode fast = head;
ListNode slow = head;
while(k-1!=0){//fast先走k-1步
fast = fast.next;
if(fast==null){
return null;//fast为空,说明k的值超出链表长度
}
k--;
}
while(fast.next!=null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
五、合并两个有序链表
1.题目
2.思路
- 1.比较两个链表的头结点的值,用一个新的结点指向小的结点
- 2.头结点和记录的tmp结点不断后移,依次连接
- 3.最后返回新结点的下一个结点
3.图解
4.解题过程
- 1.创建一个新的头结点,再用tmp代替新结点的移动
- 2.循环比较两个链表当前头结点的值,小的有tmp指向
- 3.移动tmp结点的位置,和两个链表头结点的位置
- 4.当两链表其中之一走完时,循环结束
- 5.tmp继续指向还没有走完的链表剩余的结点
- 6.返回新结点的下一个结点,即合并新链表的头结点
5.代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode head1, ListNode head2 ) {
ListNode newHead = new ListNode(0);//新创建一个结点
ListNode tmp = newHead;//tmp结点代替newHead移动
while (head1 != null && head2 != null) {
if (head1.val <= head2.val) {//判断;两个链表头结点的值
tmp.next = head1;//tmp指向head1
head1 = head1.next;//head后移
} else {
tmp.next = head2;
head2 = head2.next;
}
tmp = tmp.next;//tmp后移
}
if (head1 != null) {//如果两个链表还没走完,让tmp指向剩余结点
tmp.next = head1;
}
if (head2 != null) {
tmp.next = head2;
}
return newHead.next;//返回头结点的下一个结点
}
}