算法题目-链表

Leetcode 206. 反转链表

在这里插入图片描述
思路:从链表中依次取节点—>>头插入新链表,为了避免元素丢失,需要记录原链表没插入节点
优点:重用节点,不需要创建另外节点对象,节省空间

public ListNode reverseList(ListNode o1) {
    if (o1 == null || o1.next == null) {
        return o1;
    }
    ListNode n1 = null;
    while (o1 != null) {
        ListNode o2 = o1.next;
        o1.next = n1;
        n1 = o1;
        o1 = o2;
    }
    return n1;
}

根据值删除节点-力扣 203 题

在这里插入图片描述
思路:加哨兵,要删除得记录前一个节点,用其next指向跳过当前要删除得节点

/**
 * 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) {
        ListNode s = new ListNode(-1, head);
        ListNode pre = s;
        ListNode now = s.next;
        while(now != null){
            if(now.val == val){
                //删除,now后移
                pre.next = now.next;
                now = now.next;
            }else{
                //pre、now都后移
                pre = pre.next;
                now = now.next;
            }
        }
        return s.next;

    }
}

删除倒数节点-力扣 19 题

在这里插入图片描述
思路:要删除倒数第n个节点,就要知道倒数第n个节点的前一个节点,再将其next跳过删除节点
定义p1指向待删除节点前一个节点,p2为相对p1的后n+1个节点,p2为null时表示此时遍历链表结束,此时p1为待删除节点的前一个节点

/**
 * 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 removeNthFromEnd(ListNode head, int n) {
       //快慢指针法,p1指向待删除前一个节点,p2相对p1多走n+1步
       ListNode s = new ListNode(-1, head);
       ListNode p1 = s;
       ListNode p2 = s;
       for(int i=0; i<n+1; i++){
           p2 = p2.next;
       }
       while(p2 != null){
           p1 = p1.next;
           p2 = p2.next;
       }
       p1.next = p1.next.next;
       return s.next;
}

    
}

有序链表去重-力扣 83 题

题目数据保证链表已经按升序 排列

在这里插入图片描述
在这里插入图片描述

思路:链表升序有序,定义p1指向head,p2指向head后一个,比较p1与p2,一样则p1的next跳过p2,不一样则都向后平移

/**
 * 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 deleteDuplicates(ListNode head) {
        // ListNode s = new ListNode(-101, head);
        // ListNode p1 = s;
        // ListNode p2 = p1.next;
        // while(p2 != null){
        //     if(p1.val == p2.val){
        //         p1.next = p2.next;
        //         p2 = p2.next;
        //     }else{
        //         p1 = p1.next;
        //         p2 = p2.next;
        //     }
        // }
        // return s.next;

        
        // 链表节点 < 2
        if (head == null || head.next == null) {
            return head;
        }
        // 链表节点 >= 2
        ListNode p1 = head;
        ListNode p2;
        while ((p2 = p1.next) != null) {
            if (p1.val == p2.val) {
                p1.next = p2.next;
            } else {
                p1 = p1.next;
            }
        }
        return head;

    }
}

删除链表重复元素-力扣 82 题

在这里插入图片描述
思路:定义三节点p1、p2、p3,p1指向待删除节点的前一个节点,p2和p3用于寻找重复元素边界,p2指向重复元素开始,p3指向最后一个重复元素的下一个位置,则用p1.next指向p3即可删除全部重复元素

/**
 * 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 deleteDuplicates(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }
        ListNode s = new ListNode(-1,head);
        ListNode p1 = s;
        ListNode p2, p3;
        while((p2 = p1.next) != null && (p3 = p2.next) != null){
            if(p2.val == p3.val){
                while((p3 = p3.next) !=null && p3.val == p2.val){}
                //找到了不重复的值
                p1.next = p3;
            }else{
                p1 = p1.next;
                // p2 = p2.next;
                // p3 = p3.next;
            }


        }
        return s.next;

    }
}

合并有序链表-力扣 21 题

在这里插入图片描述
思路:依次比较两个链表的值,谁小则吧谁加入新链表,当两个链表有一个为空时,因为链表有序,直接将不为空的加入新链表即可

/**
 * 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 list1, ListNode list2) {
        ListNode s = new ListNode(-1,null);
        ListNode p = s;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                p.next = list1;
                p = p.next;
                list1 = list1.next;
            }else{
                p.next = list2;
                p = p.next;
                list2 = list2.next;
            }
        }
        if(list1 != null){
            p.next = list1;
        }
        if(list2 != null){
            p.next = list2;
        }
        return s.next;

    }
}

合并多个有序链表-力扣 23 题

在这里插入图片描述

思路:递归方式:想二叉树,多个链表,在最底层,我们将其变为两个链表,再合并两个链表,递归解决多个链表的合并(不太准确:类比左右中的遍历,左右都为单个链表,中操作为对这两个链表合并,)。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

/**
 * 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 mergeKLists(ListNode[] lists) {
    if (lists.length == 0) {
        return null;
    }
    return split(lists, 0, lists.length - 1);
}
	//返回合并后的链表,i,j代表左右边界
    public ListNode split(ListNode[] lists, int i, int j) {
        if (j == i) {
            return lists[i];
        }
        int m = (i + j) >>> 1;
        ListNode left = split(lists, i, m);
        ListNode right= split(lists, m+1, j);
        return mergeTwoLists(left, right);
    }
    public ListNode mergeTwoLists(ListNode p1, ListNode p2) {
        ListNode s = new ListNode(-1, null);
        ListNode p = s;
        while (p1 != null && p2 != null) {
            if (p1.val < p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        if (p1 != null) {
            p.next = p1;
        }
        if (p2 != null) {
            p.next = p2;
        }
        return s.next;
    }
}

查找链表中间节点-力扣 876 题

在这里插入图片描述
思路:使用快慢指针法,考虑链表个数是单数还是偶数的情况,如下图:
在这里插入图片描述

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode p1 = head;
        ListNode p2 = head;
        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
        }
        return p1;

    }
}

判断回文链表-力扣 234 题

用 O(n) 时间复杂度和 O(1) 空间复杂度解决
在这里插入图片描述
思路:找到原链表的中间位置,将此位置以后的链表(即原链表的后半段)反转,再将反转后的链表和原链表元素逐一比较,单比较到有不相等元素时表示不是回文链表,比较完都一样表示是回文链表。
在这里插入图片描述

/**
 * 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 boolean isPalindrome(ListNode head) {

        ListNode middle = middle(head);
        ListNode reverse = reverse(middle);

        while(reverse != null){
            //只要有一个不等,则返回假
            if(reverse.val != head.val){
                return false;
            }
            reverse = reverse.next;
            head = head.next;
        }
        return true;
       
    }


    //找中间节点并返回中间节点,快慢指针法
    private ListNode middle(ListNode head){
        ListNode p1 =head;//慢指针,一次一步
        ListNode p2 = head;//快指针,一次两步
        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
        }
        return p1;
    }

    //反转链表 本题不用考虑链表为空的情况,题目中链表节点做了规定
    private ListNode reverse(ListNode o1){
        ListNode n1 = null;
        while(o1 != null){
            ListNode o2 = o1.next;
            o1.next = n1;
            n1 = o1;
            o1 = o2;
        }
        return n1;
    }
}

上面算法运行时间大leetcode上拿不上高分,对其改进:将反转操作与找中间位置操作同步进行,反转的是前半部分,当找到中间位置时也反转了前半部分,用反转的前半部分与后半部分比较(后半部分的开始就是此时的中间位置即慢指针此时所指的位置。但注意原链表基数和偶数对结果的影响:
在这里插入图片描述
当原链表为奇数时,让p1(中间节点的后一个位置)与n1节点中的值逐一比较。

/**
 * 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; }
 * }
 */
 //思路:找到原链表的中间位置,将此位置以后的链表(即原链表的后半段)反转,
//  再将反转后的链表和原链表元素逐一比较,当比较到有不相等元素时表示不是回文链表,
//  比较完都一样表示是回文链表。但要注意基数偶数情况
//1->2->3->4->5  基数  此时p2不为null
//12  345
//1->2->3->4->5->6  偶数
//123   456
class Solution {
    public boolean isPalindrome(ListNode head) {

        ListNode p1 = head;//慢指针
        ListNode p2 = head;//快指针
        ListNode n1 = null;//新头
        ListNode o1 = head;//旧头
        //实现反转前半部分链表与找中间位置
        while(p2 != null && p2.next != null){

            p1 = p1.next;
            p2 = p2.next.next;

            ListNode o2 = o1.next;
            o1.next = n1;
            n1 = o1;
            o1 = o2;

        }
        //判断原链表是基数还是偶数
        if(p2 != null){//奇数节点
            p1 = p1.next;
        }
        while(n1 != null){
            if(n1.val != p1.val){
                return false;
            }
            n1 =n1.next;
            p1 = p1.next;
        }
        return true;
    }         
    
}

龟兔赛跑算法

在这里插入图片描述
如果链表上存在环,那么在环上以不同速度前进的两个指针必定会在某个时刻相遇。算法分为两个阶段:

阶段1

  • 龟一次走一步,兔子一次走两步
  • 当兔子能走到终点时,不存在环
  • 当兔子能追上龟时,可以判断存在环

阶段2

  • 从它们第一次相遇开始,龟回到起点,兔子保持原位不变
  • 龟和兔子一次都走一步
  • 当再次相遇时,地点就是环的入口

为什么呢?
当它们第一次相遇时,直线上兔子都比龟快,所以相遇只能在环中,它们都走了直线距离,而兔子走的是龟的两倍,兔=2龟---->兔-龟=龟---->直线+n圈+k(相遇点距离入口距离)-(直线+m圈+k)=(n-m)圈,也是绕环的倍数,
入口:对于龟来说=直线+绕环倍数圈
此时相遇的位置就是绕环(n-m)圈,所以在此位置再走一个直线距离就是入口。

环形链表-力扣 141 题

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next !=null){

            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                return true;
            }

        }
        return false;
    }
}

环形链表检测入口-力扣 142 题

在这里插入图片描述
易错点:当我们在第二个阶段找入口时,将龟放入起点,此时若兔子也在起点时表示他们的第二此相遇,此时就是环的入口。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    // public ListNode detectCycle(ListNode head) {

    //     ListNode fast = head;
    //     ListNode slow = head;
    //     while(fast != null && fast.next !=null){

    //         slow = slow.next;
    //         fast = fast.next.next;
    //         if(slow == fast){//进入第二阶段
    //             slow = head;
    //             while(true){
    //                 fast = fast.next;
    //                 slow = slow.next;
    //                 if(fast == slow){
    //                     return slow;
    //                 }              
    //             }
    //         }

    //     }
    //     return null;
        
    // }
    public ListNode detectCycle(ListNode head) {

        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next !=null){

            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){//进入第二阶段
                slow = head;
                while(true){
                    if(fast == slow){//当此时龟放到起点,而恰好兔子也在起点是说明
                                    //这是第二此相遇,直接返回入口即可(这种情况是没有直线只有环的
                                    //情况)
                        return slow;
                    }   
                    fast = fast.next;
                    slow = slow.next;           
                }
            }

        }
        return null;
        
    }
}

return 语句的作用域是整个函数,而不仅仅是内层循环。当执行 return 语句时,函数会立即结束,并返回指定的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值