链表算法题

1.反转链表(重点)

反转链表: 输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

假设链表为 1→2→3→∅,反转后为 ∅←1←2←3。
在遍历链表时,将当前节点的next指针改为指向前一个节点。
由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。
在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

public ListNode reverseList(ListNode head) {
            ListNode pre=null;
            ListNode cur=head;
            while (cur!=null){
                ListNode next=cur.next;
                cur.next=pre;//让当前节点指向上一个节点
                pre=cur;//将当前节点保存到上一个当中
                cur=next;
            }
            return pre;
        }

2.合并两个排序的链表

NowCoder

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null && l2 == null) return null;
        ListNode head=new ListNode(-1);
        ListNode temp=head;
        while (l1!=null && l2 !=null){
            if(l1.val > l2.val){
                temp.next=l2;
                l2=l2.next;
            }else {
                temp.next=l1;
                l1=l1.next;
            }
            temp=temp.next;
        }
        temp.next= l1==null? l2:l1;
        return head.next;
    }

3.删除链表节点

https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/

    public ListNode deleteNode(ListNode head, int val) {
        if(head == null) return null;
        if(head.val==val) return head.next;
        //初始化双指针
        ListNode pre=head;
        ListNode cur=head.next;
        //根据题意绝对找得到 所以无需判断是否为空
        while (cur.val != val){
            pre=pre.next;
            cur=cur.next;
        }
        pre.next = cur.next;
        return head;
    }

4.删除链表中重复的结点(重点)

NowCoder

 public ListNode deleteDuplication(ListNode pHead){
       if(pHead == null || pHead.next == null)  return pHead;
        // 自己构建辅助头结点
        ListNode head = new ListNode(Integer.MIN_VALUE);
        head.next = pHead;
        ListNode pre = head;
        ListNode cur = head.next;
        while(cur!=null){
            if(cur.next != null && cur.next.val == cur.val){
                // 相同结点一直前进
                while(cur.next != null && cur.next.val == cur.val)
                    cur = cur.next;
                
                // 退出循环时,cur 指向重复值,也需要删除,而 cur.next 指向第一个不重复的值
                cur = cur.next;
                // pre 连接新结点
                pre.next = cur;
            }else{
                pre = cur;
                cur = cur.next;
            }
        }
        return head.next;
    }

	//方法2
	public ListNode deleteDuplication(ListNode pHead){
       if(pHead == null || pHead.next == null) return pHead;
        ListNode newHead=new ListNode(-1);
        ListNode p=newHead,cur=pHead;
        while(cur!=null){
            //如果遇到一样的了 
            if(cur.next != null && cur.next.val==cur.val){
                //就让他一直到最后一个一样的位置
                while(cur.next != null && cur.next.val==cur.val)
                    cur=cur.next;
                //由于重复的元素一个也不要 所以最后一个也不要
                cur=cur.next;
            }
            else{
                p.next=cur;
                p=p.next;
                cur=cur.next;
            }
        }
    //这句话很重要 因为有可能最后几个元素都是重复的 而上面循环的p有可能没走到末尾 比如 {1,2,3,3,4,5,5}
        p.next=null;
        return newHead.next;

5.快慢指针问题(重点)

1.链表的中间节点

链表的中间节点

2.链表中环的入口结点

NowCoder 力扣

使用双指针,一个快指针 fast 每次移动两个节点,一个慢指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。查找有环链表中环的入口结点。当快慢指针相遇时,我们可以判断到链表中有环,这时重新设定一个新指针指向链表的起点,且步长与慢指针一样为1,则慢指针与“新”指针相遇的地方就是环的入口。

  public ListNode detectCycle(ListNode head) {
        if(head == null) return null;
        ListNode slow=head;
        ListNode fast=head;
        boolean flag=false;
        while (fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast == slow){
                flag=true;
                break;
            }
        }
        if(!flag) return null;//遍历完了都没找到说明为null
        //这个时候定义一个新节点从开始遍历
        ListNode cur=head;
        while (cur!=slow){
            cur=cur.next;
            slow=slow.next;
        }
        return cur;
    }

3.链表中倒数第 K 个结点

https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

设链表的长度为 N。设置两个指针 fast和 slow,先让 fast移动 K 个节点,则还有 N - K 个节点可以移动此时让 fast和 slow同时移动,可以知道当 fast移动到链表结尾时,slow移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。

    public ListNode getKthFromEnd(ListNode head, int k) {
     if(head == null) return null;
        ListNode slow=head;
        ListNode fast=head;
        for (int i = 0; i < k; i++) 
            fast=fast.next;
        while (fast !=null){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }

4.回文链表

力扣地址:判断一个链表是否是回文结构

方法一:通过栈来把所有的数字压进去 然后在遍历一遍依次比较

方法二:快慢指针 + 反转链表。通过快慢指针先让慢指针走到中点 然后反转剩下的部分。反转完成之后 然后再定义一个变量从头开始依次和反转那部分的链表进行比较是否相等

public boolean isPalindrome(ListNode head) {
        if(head == null) return false;
        if(head.next==null) return true;
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        //遍历完成之后,slow指向中间节点

        ListNode pre=null;
        ListNode cur=slow;
        while(cur!=null){
            ListNode next=cur.next;
            cur.next=pre;
            pre=cur;
            cur=next;
        }
        //反转完成之后 pre就为1->2 然后依次遍历2个链表相互比较
    
        ListNode p2=pre;
        ListNode p1=head;
        while(p2!=null){
            if(p1.val != p2.val)  return false;//只要有一个不相等则返回false
            p1=p1.next;
            p2=p2.next;    
        }
        return true;
    }

5.相交链表

相交链表: 找到两个单链表相交的起始节点。

思想:先统计两个链表的长度,然后相减这就是他们的步长差,然后让长的先走步长差的长度,然后两个链表再一起移动,相交则为相交节点

	public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB==null) return null;
        ListNode l1=headA;
        ListNode l2=headB;
        int countA=0,countB=0;
        while(l1!=null){
            countA++;
            l1=l1.next;
        }
          while(l2!=null){
            countB++;
            l2=l2.next;
        }
        ListNode a=headA;
        ListNode b=headB;
        int skip=Math.abs(countA-countB);
        if(countA>countB)
            while(skip>0) {
               a=a.next; 
               skip--;
            }
        else if(countA<countB)
            while(skip>0) {
               b=b.next; 
               skip--;
            }

        while(a!=null && b!=null){
            if(a==b) return a;
            a=a.next;
            b=b.next;
        }
        return null;
    }

6.排序链表(快慢指针+合并链表+归并)

牛客地址:归并排序,通过快慢指针,找到中间节点 然后分割

   public ListNode sortInList (ListNode head) {
        if(head==null || head.next==null) return head;
        return mergeSort(head);
    }
    public ListNode mergeSort(ListNode head){
        //当只有一个节点,直接返回
        if(head.next == null) return head;
        ListNode slow=head,fast=head,pre=null;
        //找到中间节点
        while(fast!=null && fast.next!=null){
            pre=slow;
            slow=slow.next;
            fast=fast.next.next;
        }
        //断开
        pre.next=null;
        //此时slow来到中间节点
        ListNode r= mergeSort(slow);
        ListNode l= mergeSort(head);
        return merge(r,l);
    }
    
    
    //合并链表
    public ListNode merge(ListNode head1,ListNode head2){
        ListNode l1=head1;
        ListNode l2=head2;
        ListNode tempNode=new ListNode(-1);
        ListNode temp=tempNode;
        while(l1!=null && l2!=null){
            if(l1.val < l2.val){
                temp.next=l1;
                l1=l1.next;
            }else{
                temp.next=l2;
                l2=l2.next;
            }
            temp=temp.next;
        }
        temp.next=l2==null?l1:l2;
        return tempNode.next;
    }
}

6.旋转右移链表

力扣地址

思路:先统计有多少个数,然后判断需要移动几个位置,让他变成循环链表,然后移动指定的步长,然后破除循环

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
            if(head==null)
                return null;
            else {
                ListNode node=head;
                ListNode listNode=new ListNode(0);
                int count=0;//总共多少数
                while (node.next!=null){
                    node=node.next;
                    count++;
                }
                count++;
                int flag=count - k % count+1;//移动几个位置
                node.next=head;//变成循环链表
                ListNode temp=head;
                for (int i = 1; i < flag; i++)
                    temp=temp.next;
                listNode.next=temp;//让新链表头结点指向当前的头结点
                //破除循环链表
                ListNode cur=listNode;
                for (int i = 0; i < count; i++)
                    cur=cur.next;
                cur.next=null;
                return listNode.next;
            }
    }
}

7.分隔链表

将单向链表按某值划分成左边小、中间相等、右边大的形式

思路:定义6个空的链表指针,分别为左中右,然后比较大小放在对应的链表位置,最终串起来就行。

这是一道简单的分隔链表:对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

	public ListNode partition(ListNode head, int x) {
        if(head == null) return null;
        ListNode left=new ListNode(-1);
        ListNode lp=left;
        ListNode right=new ListNode(-1);
        ListNode rp=right;
        while(head!=null){
            if(head.val < x){
                lp.next=head;
                lp=lp.next;
            }else {
                rp.next=head;
                rp=rp.next;
            }
            head=head.next;
        }
        rp.next=null;//让大的一方下一个指向null 免得造成了循环链表
        //遍历分组完成 然后判断连个组有不有空的,然后串起来
        if(left.next == null) return right.next;//如果左边分组为空 则直接返回右边的
        if(right.next == null) return left.next;//如果右边分组为空 则直接返回左边的
        lp.next=right.next;//否则就串起来
        return left.next;
    }

8.重排链表(牛客重点)

牛客地址:反转链表+快慢指针+合并链表

public void reorderList(ListNode head) {
        if(head == null || head.next==null) return ;
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        ListNode mid = slow.next;//中间节点
        ListNode newHead =getReverse(mid);
        slow.next=null;//将head链表的一半之后置为null
        mergeList(head,newHead);
    }
    //合并链表
    public void mergeList(ListNode l1, ListNode l2) {
        ListNode l1_next;
        ListNode l2_next;
        while (l1 != null && l2 != null) {
            //分别获得他们下一个
            l1_next = l1.next;
            l2_next = l2.next;
 			//交错相连
            l1.next = l2;
            l1 = l1_next;
 
            l2.next = l1;
            l2 = l2_next;
        }
    }
    
    //反转链表
    public ListNode getReverse(ListNode head){
        ListNode pre=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode next=cur.next;
            cur.next=pre;
            pre=cur;
            cur=next;
        }
        return pre;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值