链表浅谈

基础数据结构--链表

导语

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

常见链表结构

链表结构五花八门,常见的链表结构有:单链表、双向链表和循环链表。

  • 单链表
    单链表
    单链表中两个结点比较特殊,分别是第一个结点和最后一个结点,我们习惯称第一个结点叫头结点把最后一个结点称尾结点,其中头结点用来记录链表的基地址,尾结点比较特殊指针不是指向下一个结点合适一个空地址NULL
  • 循环链表
    循环链表
    循环链表是一种特殊的单链表,尾结点指针指向头结点地址
  • 双向链表
    双向链表
    顾名思义双向链表支持两个方向,每个节点不止有一个后继指针next指向后边的结点,还有一个前驱指针prev指向前面的结点
    双向链表的优点
    在实际开发中,从链表中删除一个数据有两种情况:
    1:删除结点中“值等于某个给定值”的结点
    2:删除给定指针指向的结点
    对于第一种情况不管是单链表还是双向链表,为了查找到值等于给定值的结点,都需要从头结点开始一个一个遍历对比,直到找到值等于给定值得结点。然后在通过指针操作将其删除。时间复杂度分析,时间消耗在遍历数值上面复杂度为O(n);
    第二种情况,我们已经找到了要删除的结点,由于单链表不支持直接获取他的前驱结点,所以为了找到该结点的前驱结点我们还是要从头遍历链表,直到p->next=q,说明p是q的前驱结点,然后在通过指针进行删除操作。双向链表一个结点中保存了它的前驱结点prev,所以很容易进行删除操作。时间复杂度分析O(1);

链表常见操作

  • 链表实现LRU缓存淘汰算法
    链表缓存策略
  • 单链表反转

头结点插入法

public static ListNode reverseListByInsert(ListNode listNode){
        ListNode resultList = new ListNode(-1); //定义一个哨兵结点
        ListNode p = listNode;
        while (p!= null){
            ListNode tempNodeList = p.next;
            p.next = resultList.next;
            resultList.next = p;//插入到哨兵结点
            p = tempNodeList;
        }
        return resultList.next;
    } 

就地反转法

public static ListNode reverseListByLocal(ListNode listNode) {
       ListNode resultNode = new ListNode(-1);
       resultNode.next = listNode;
       ListNode q = listNode;
       ListNode qNext = q.next;
       while (qNext != null) {
           q.next = qNext.next;//q 数据永远不变,指针随着遍历游标后移
           qNext.next = resultNode.next; //获取反转后驱结点
           resultNode.next = qNext;// 哨兵结点保存下反转后的"前驱"结点
           qNext = q.next;
       }
       return resultNode.next;
   }
  • 链表中环的检测

快慢指针

    public static boolean hasLoop(ListNode listNode) {
        if (listNode == null) return false;
        ListNode fastPoint = listNode;
        ListNode slowPoint = listNode.next;
        while (fastPoint != null && slowPoint != null) {
            fastPoint = fastPoint.next;///指针移动一位
            slowPoint = slowPoint.next.next;//指针移动两位
            if (fastPoint == null) {
                return false;
            } else if (fastPoint == slowPoint) {
                return true;
            }
        }
        return false;
    }

跟踪记录法

    private static HashMap<ListNode, Integer> nodeMap = new HashMap<>();

    public static boolean hasLoopV2(ListNode listNode, int index) {
        if (listNode == null || listNode.next == null) return false;//最少三个成环
        if (nodeMap.containsKey(listNode)) {
            return true;
        } else {
            nodeMap.put(listNode, index);
            return hasLoopV2(listNode.next, ++index);
        }
    }
  • 两个有序链表合并,合并后仍有序

有序同向链表合并

    public static ListNode mergeTwoList(ListNode head1, ListNode head2) {
        if (head1 == null && head2 == null) return null;
        if (head1 == null) return head2;
        if (head2 == null) return head1;
        ListNode head;
        //此次演示的是两个递增链表,如果两个同为递减链表,则if判断句为head1.data < head2.data
        if (head1.data > head2.data) {
            head = head2;
            head.next = mergeTwoList(head1, head2.next);
        } else {
            head = head1;
            head.next = mergeTwoList(head1.next, head2);
        }
        return head;
    }

反向链表可以先进行链表反转,然后在同向合并

  • 删除链表倒数第n个结点

双指针法

    public static ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode preNode = head;
        ListNode curNode = head;
        for (int i = 0; i < n; i++) {
            preNode = preNode.next;
            if (preNode == null && i != (n - 1)) return null;//判断是否超出最大个数
        }

        if (preNode == null) {//删除头结点判断
            ListNode tempList = curNode.next;
            curNode.next = null;
            return tempList;
        }

        //两个指针,先到尾结点停止循环,后指针的下一个即为要删除的结点
        while (preNode.next != null) {
            curNode = curNode.next;
            preNode = preNode.next;
        }
        curNode.next = curNode.next.next;
        return head;
    }
  • 求链表中的间结点

快慢指针

    public static ListNode middleNode(ListNode listNode) {
        if (listNode.next == null) return listNode;
        ListNode fastNode = listNode.next.next;
        if (fastNode == null) return listNode.next;
        ListNode slowNode = listNode;
        while (fastNode != null) {
            fastNode = fastNode.next.next;
            slowNode = slowNode.next;
        }
        return slowNode;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值