[算法] 链表

        欢迎来到老胡的算法解题思路,本文章主要使用的语言为java,使用的题型为力扣算法题,基于这一篇文章,我将为你介绍链表的基础知识和链表题型,喜欢的朋友可以关注一下,下次更新不迷路!

目录

前言

一、创建链表类

二、链表题型

2.1、修改链表结点

2.2、合并有序链表

2.3、寻找单链表结点

2.4、链表相交问题

2.5、链表反转

三、总结


前言

        链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。


一、创建链表类

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; }
  }

二、链表题型

2.1、修改链表结点

例题:力扣82.删除排序链表中的重复元素||

 分析:题目分析

        这类题型涉及到链表结点的修改,本题主要删除链表中重复出现的结点,针对本题,由于本题给定条件是已经排序好的链表,因此重复元素出现的位置也是连续的,我们可以采用一次遍历的方法来解题。

分析:解题模板

while (条件) {
        if () {
        // 删除结点
        node.next = node.next.next;
        }
        } else {
        //   指向下一个新的且在链表中没有重复出现的结点
        node = node.next;
        }

解题:完整代码

class Solution {
      public ListNode deleteDuplicates(ListNode head) {
            // 给给定的链表设置头指针
            ListNode listNode = new ListNode(0, head);
            // 给该链表设置移动指针
            ListNode node = listNode;
            while (node.next != null && node.next.next != null) {
                  if (node.next.val == node.next.next.val) {
                        int count = node.next.val;
                        while (node.next != null && node.next.val == count) {
                            // 删除结点
                              node.next = node.next.next;
                        }
                  } else {
                    //   指向下一个新的且在链表中没有重复出现的结点
                        node = node.next;
                  }
            }

            return listNode.next;
      }
}

2.2、合并有序链表

例题:力扣23.合并k个升序链表

分析:题目分析

        合并 k 个有序链表的逻辑类似合并两个有序链表,难点在于,如何快速得到 k 个节点中的最⼩节点?这⾥我们就要⽤到 优先级队列 这种数据结构,把链表数值放入优先队列,然后在通过读取出来的值创建新发符合要求的链表即可。

解题:完整代码

class Solution {
    ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0){
            return null;
        }
        // 虚拟头结点指针和头节点
        ListNode p = new ListNode();
        ListNode dummy = new ListNode(-1,p);      
        // 创建优先队列
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        // 将 k 个链表的值加入优先队列
        for (ListNode head : lists) {
            while(head!=null){
                pq.add(head.val);
                head = head.next;
            }
        }
        while (!pq.isEmpty()) {
        // 获取最⼩值,创建新链表
            ListNode node = new ListNode();
            node.val = pq.poll();
            p.next = node;
            p=p.next;
            }
            return dummy.next.next;
    }
}

2.3、寻找单链表结

例题:力扣1019.链表中的下一个更大节点 

分析:题目分析

        针对这类题目,我们只需要给两个指针,对两个指针指向的结点的值进行比较,进行两次遍历,满足条件跳出循环,最后在把结果转换为题目所需要的类型即可。

解题:完整代码

class Solution {
    public int[] nextLargerNodes(ListNode head) {
        int count = 0;
        //头结点
        ListNode listNode = new ListNode(Integer.MIN_VALUE,head);
        //虚拟指针
        ListNode node = head;
        // 两层遍历
        while(head!=null){
            count++;
            node = head;
            while(node.next!=null){
                //找到值,跳出循环
                // 到达边界,也跳出循环,最后一个值交给循环外处理
                if(node.next.val>head.val){
                    head.val=node.next.val;
                    break;
                }
                node = node.next;
            }
            //1.处理二层循环找不到最大值
            //2.处理一层循环的最后一位数
            if(node.next == null){
                head.val = 0;
            }
            head = head.next;
        }
        //将结果转换为数组
        int[] ans = new int[count];
        for(int i = 0;listNode.next!=null;i++){
            ans[i] = listNode.next.val;
            listNode = listNode.next;
        }
        return ans;
    }
}

2.4、链表相交问题

例题:力扣142.环形链表||

 分析:题目分析

        本题是比较典型的环形链表,我们可以借助哈希表遍历链表中的每个节点,并将它记录下来;一旦遇到了遍历过的节点,就可以判定链表中存在环。从而通过头指针指出位置并输出。

解题:完整代码

public class Solution {
    public ListNode detectCycle(ListNode head) {
        //构建哈希表
        Map<ListNode,Integer> map = new HashMap<>();
        int count = 0;
        //头指针
        ListNode node = head;
        while(head!=null){
            // 判断哈希表是是否含有同一个结点
            if(map.get(head)==null){
                map.put(head,count);
                count++;
                head = head.next;
            }else{ 
                // 跳出循环,并给出循环处的索引
                count = map.get(head);
                break;
            }
        }
        // 移动头指针,寻找循环结点
        for(int i = 0;i<count;i++){
                node = node.next;
            }
            return node;
    }
}

2.5、链表反转

例题:力扣206.反转链表

 分析:题目分析

        这类题主要作为面试考题,主要考察基础知识的掌握,在创建链表时,主要有两种方法,头插法和尾插法,针对这种题型,只需要一次遍历后用尾插法的方式并加入节点值。

解题:完整代码

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode listNode = new ListNode();
        ListNode ans = listNode;
        while(head!=null){
            ListNode node = new ListNode();
            node.val = head.val;
            node.next = listNode.next;
            listNode.next = node;
            head = head.next;
            
        }
        return ans.next;

    }
}


三、总结

        使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理,同时链表允许插入和移除表上任意位置上的 节点 ,但是不允许 随机存取 。缺点是链表失去了数组随机读取的优点,并且由于增加了结点的指针域,空间开销比较大。

补充说明:链表还存在很多没有总结到的典型题型,后期总结后继续补上

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值