IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> leetcode 链表专题 -> 正文阅读

[数据结构与算法]leetcode 链表专题

?(1)力扣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 head1, ListNode head2)
    { 
       //创建一个新链表,将两个要合并的链表都插入到这个新的链表里面
       ListNode dummy=new ListNode(0);
       ListNode cur=dummy;
       while(head1!=null&&head2!=null)
       {
           //谁小谁插入
           if(head1.val<=head2.val)
           {
               cur.next=head1;
               head1=head1.next;
           }
           else
           {
               cur.next=head2;
               head2=head2.next;
           }
           cur=cur.next;
       }  
       //跳出这个循环之后,有可能某一个链表还不为空
       if(head1!=null)   cur.next=head1;
       if(head2!=null)   cur.next=head2;
       return dummy.next;

    }
}

(2)力扣86 分隔链表

class Solution
{
    public ListNode partition(ListNode head, int x)
    {
        //把原链表分成两个小链表,一个链表中的元素大小都小于 x,另一个链表中的元素都大于等于 x
        //现在有三个链表:新链表,链表1,链表2
        //遍历原链表

        //链表1,用来装比x小的元素
        ListNode dummy1=new ListNode(-1);
        ListNode p1=dummy1;
        //链表2,用来装大于等于x的元素
        ListNode dummy2=new ListNode(-1);
        ListNode p2=dummy2;
        
        ListNode p=head;
        while(p!=null)
        {
            //大的加到链表2里面去
            if(p.val>=x)
            {
                p2.next=p;
                p2=p2.next;
                p=p.next;
                p2.next=null;
            }
            else
            {
                p1.next=p;
                p1=p1.next;
                p=p.next;
                p1.next=null;
            }
        }
        p1.next=dummy2.next;
        dummy2.next=null;
        return dummy1.next;
    }
}

?(3)力扣23 合并k个升序列表

利用最小堆找出k个链表第一个节点中,值最小的节点

每个链表把各自的第一个节点送进最小堆里面,最小的节点才能添加进新链表里面

class Solution
{
    public ListNode mergeKLists(ListNode[] lists) 
    {
        if (lists.length == 0)  return null;
        //
        ListNode dummy=new ListNode(-1);

        ListNode p=dummy;

        //最小堆的堆顶元素是最小(而且比较的是节点的值),只有最小值才可以送到新链表里面去
        PriorityQueue<ListNode> queue=new PriorityQueue(
                                                         new Comparator<ListNode>()
                                                         {
                                                             public int compare(ListNode a,ListNode b)
                                                             {
                                                                 return a.val-b.val;
                                                             }
                                                         }
                                                       );

        //把每个链表的第一个节点放进队列里面,初始化队列
        for(ListNode head:lists)
        {
            if(head!=null)
            {
               queue.add(head);
            }
        }
       //出一个最小堆的堆顶元素(一个节点)就要将这个堆顶元素的下一个节点入堆
       while(queue.size()!=0)
       {
           ListNode node=queue.poll();
           p.next=node;
           
           //如果这个这个刚刚出堆的堆顶元素有下一个节点,就把它的下一个节点放进堆里补位
           if(node.next!=null)
           {
               queue.add(node.next);
           }
           p=p.next;

       }
       return dummy.next;
    }
}

(4)力扣19 删除链表的倒数第k个结点

遍历两次肯定是可以得到结果的,如果想只遍历一次就得到结果:双指针

两个指针起始时,同时指向dummy结点,然后其中一个指针向后走k步,另一个指针向前走1步,这样两个指针之间就相差k-1步(也就是慢指针前进k-1步就能到达快指针的位置),两个指针一前一后,然后两个指针一起向后走,当后面的结点跑到最后一个位置的时候,前面的指针指向的就是倒数第k个节点

class Solution
{
    public ListNode removeNthFromEnd(ListNode head, int k) 
    {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode slow = dummy;
        ListNode fast = dummy;
        
        //fast指针先前进k步
        for(int i=0;i<k;i++)
        {
          fast=fast.next;
        }
        //slow指针前进1步
       slow=slow.next;    

       //prev指针指向slow指针指向节点的上一个结点,便于删除
       ListNode prev = dummy;

        while (fast.next != null) 
        {
            fast = fast.next;            
            prev = slow;
            slow = slow.next;
        }
        
        //删除slow指针指向的结点
        prev.next = slow.next;

        // 释放 待删除节点slow 的next指针, 这句删掉也能AC
        slow.next = null;
        return dummy.next;
    }
}

(5)876. 链表的中间结点 - 力扣(LeetCode)

两个指针?slow?和?fast?分别指向链表头结点?head

每当慢指针?slow?前进一步,快指针?fast?就前进两步,这样,当?fast?走到链表末尾时,slow?就指向了链表中点

class Solution 
{
    public ListNode middleNode(ListNode head)
    {
     
        ListNode slow=head;
        ListNode fast=head;
        while(fast.next!=null)
        {
            fast=fast.next;

            //通过[1,2,3,4,5,6]这个测试用例可以发现存在下面这种情况:
            //有可能只前进一步fast就到达了最后一个结点了,此时不能再前进了,再前进就会报越界异常了
            if(fast.next==null)
            {
               slow=slow.next;
               break;
            }
            else
            {
                fast=fast.next;
                slow=slow.next;
            } 
        }
        return slow;
    }
}

(6)力扣237? 删除链表中的结点

注意这道题目中传入的node就是我们要删除的结点

最后跳出循环的时候:

class Solution 
{
    public void deleteNode(ListNode node) 
    {
        ListNode p=node;
        ListNode q=node.next;
        while(q.next!=null)
        {
            p.val=q.val;
            p=q;
            q=q.next;
        }
        p.val=q.val;
        p.next=null; 
    }
}

(7)力扣141 环形链表,判断链表是否有环

方法一:


public class Solution
 {
    public boolean hasCycle(ListNode head) 
    {
        if(head==null)   return false;
        HashSet set=new HashSet();
        while(head!=null)
        {
            if(set.contains(head)) return true;
            else{
                 set.add(head);
                 head=head.next;
            }
        }
        return false;
    }
}

方法二:

每当慢指针?slow?前进一步,快指针?fast?就前进两步。

如果?fast?最终遇到空指针,说明链表中没有环;如果?fast?最终和?slow?相遇,那肯定是?fast?超过了?slow?一圈,说明链表中含有环

public class Solution
 {
    public boolean hasCycle(ListNode head) 
    {
       ListNode slow=head,fast=head;

       //一直往前移,直到fast=null
       while(fast!=null)
       {
           //slow移动一步,fast移动两步
           slow=slow.next;
           fast=fast.next;

           //进入这个循环,fast是不为null的,但是fast=fast.next后不能保证fast不为null
           //如果只走一步就为null了,直接返回false
           //第一步不为null才能走第二步
           if(fast==null)
           {
              return   false;
           }
           else
           {
               fast=fast.next;
           }


           if(slow==fast)   return true;
       }
       return false;
    }
}

(8)力扣链表中环的入口

上一个问题的进阶版:如果链表有环,返回这个环的起点

方法一:

public class Solution 
{
    public ListNode detectCycle(ListNode head) 
    {
        if(head==null)   return null;
        HashSet set=new HashSet();
        while(head!=null)
        {
            if(set.contains(head)) return head;
            else
            {
                 set.add(head);
                 head=head.next;
            }
        }
        return null;
    }
}

方法二:

当快慢指针相遇时,让其中任一个指针指向头节点(不是dummy结点,而是第一个有效的结点),然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置

public class Solution 
{
    public ListNode detectCycle(ListNode head) 
    {
       ListNode slow=head,fast=head;
       while(fast!=null)
       {
           slow=slow.next;
           fast=fast.next;
           if(fast!=null)
           {
               fast=fast.next;
           }
           else
           {
               return  null;
           }

           //有环了,slow等于head,然后slow,fast速度一致往前走,再次相遇的位置就是环的入口
           if(slow==fast)
           {
               slow=head;
               while(slow!=fast)
               {
                   slow=slow.next;
                   fast=fast.next;
               }
               return slow;
           }
       }
       return null;
    }
}

(10)160. 相交链表 - 力扣(LeetCode)两个链表是否相交

需要返回两个链表相交的起始结点

方法一:


public class Solution 
{
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) 
    {
        HashSet set=new HashSet();
        while(headA!=null)
        {
           set.add(headA);
           headA=headA.next;
        }
        while(headB!=null)
        {
            if(set.contains(headB))    return headB;
            headB=headB.next; 
        }
        return null;      
    }
}

方法二:

用两个指针?p1?和?p2?分别在两条链表上前进

p1?遍历完链表?A?之后开始遍历链表?B,让?p2?遍历完链表?B?之后开始遍历链表?A

?最终p1走过的距离是m+k+n,而p2走过的距离是n+k+m

?这道题存在的一个坑在于:

?这种情况,两个链表没有相交的部分时,容易陷入死循环,超时

解决方法:定义一个计数器count,p1指针遍历完A链表再跳到B链表,count++

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? p2指针遍历完B链表再跳到A链表,count++

这样count=2了,如果没有相交部分,那count还要++,所以如果count>2了,那就说明这两个链表没有相交的部分,返回null

public class Solution 
{
    public ListNode getIntersectionNode(ListNode headA, ListNode headB)
    {
        ListNode p1=headA,p2=headB;
        int count=0;

        if(p1==null||p2==null)  return null;
        //确保两个链表都不是空链表

        while(p1!=p2)
        {
            if(count>2)  return null;
            
            //p1指针下一个到底指向哪一个结点,是直接p1.next,还是headB
            if(p1.next==null)
            {
                p1=headB;
                count++;
            }
            else
            {
                p1=p1.next;
            }

            if(p2.next==null)
            {
                p2=headA;
                count++;
            }
            else
            {
                p2=p2.next;
            }    
        }
        return p1;      
    }
}

(11)力扣707 设计链表

①解释题目:就是实现五个功能:获取链表中第index个结点的值,在表头添加元素,在表尾添加元素,在第index个结点之前添加结点,删除第index个结点

②这里定义了一个size,初始等于0,用来记录链表的长度,每次向链表中添加一个结点就size++,删除一个结点就size--

③由于index从0开始计数,所以size=最大的index+1

?④跳出循环的时候current指向哪个结点

初始的时候current=dummy

while(int i=0;i<index;i++)
{
 current=current.next;
}

?有了这张表就很清晰(从第0轮开始)每i轮最终current会到达第i个结点

?所以可以判断:跳出这个循环,current=index-1,因为i=index的时候进不去循环

class MyLinkedList
{
      //dummy结点
      ListNode dummy;
      
      //用size来记录链表的长度,执行添加结点操作时size++
      int size;
     
    // 构造方法.初始化size和summy两个参数
    public MyLinkedList() 
    {
        size=0;
        dummy=new ListNode(0);  
    }
    
    //获取第index个结点的数值
    public int get(int index) 
    {
       if(index<0||index>=size)  return -1;

       ListNode current=dummy;
       for(int i=0;i<=index;i++)
       {
         current=current.next;
       }
       return current.val;
    }
    
    //链头添加一个结点
    public void addAtHead(int val) 
    {
        ListNode new_node=new ListNode(val);
        new_node.next=dummy.next;
        dummy.next=new_node;
        size++;
    }
    
    //链尾添加一个结点
    public void addAtTail(int val) 
    {
        ListNode new_node=new ListNode(val);
        ListNode current=dummy;
        for(int i=0;i<size;i++)
        {
            current=current.next;
        }
        current.next=new_node;
        size++;
    }

    //在第 index 个节点之前添加节点
    public void addAtIndex(int index, int val) 
    {
        //如果index小于0,在头部插入节点(直接调用前面写好的addAtHead函数)
        if(index<0)   addAtHead(val);
        //如果 index 等于链表的长度,在链表末尾添加元素,可以直接调用前面写好的addAtTail函数
        if(index==size)  addAtTail(val);
        //如果 index 大于链表长度,则不会插入节点
        if(index>size)  return;

        ListNode new_node=new ListNode(val);
        ListNode current=dummy;
        for(int i=0;i<index;i++)
        {
            current=current.next;
        }
        //跳出循环,此时current指向的是第index-1个结点
        new_node.next=current.next;
        current.next=new_node;
        size++;
    }
    
    public void deleteAtIndex(int index) 
    {
        if(index<0||index>=size)  return;

        ListNode current=dummy;
        for(int i=0;i<index;i++)
        {
            current=current.next;
        }
        //跳出循环,此时current指向的是第index-1个结点
        current.next=current.next.next;
        size--;
    }
}

?(12)剑指 Offer 24. 反转链表 - 力扣(LeetCode)反转链表

?

class Solution
 {
    public ListNode reverseList(ListNode head) 
    {
        if(head==null)  return null;
        ListNode dummy=new ListNode(-1);
        while(head!=null)
        { 
            //temp用来标记这一轮要插入结点的下一个结点(也就是下一轮要插入的结点,要不然下一轮找不到位置)
            ListNode temp=head.next;
           
            //这两行就是头插法插入结点到dummy结点的后面
            head.next=dummy.next;
            dummy.next=head;

            head=temp;
        }
        return dummy.next;
    }
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:48:39  更:2022-07-17 16:51:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 9:01:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计