链表基础(链表、数组性能对比、链表翻转、回文子串)

链表的类型

链表也是线性表的一种

  • 单链表
  • 双向链表
  • 循环链表
  • 双向循环链表

链表/数组的性能对比

  • 随机取的时间复杂度
    • 数组:通过下标取是 O(1);取指定值是 O(n)
    • 链表:因为链表的内存地址不连续,所以没办法通过下标取;取指定值是 O(n)
  • 插入/删除
    • 数组:涉及到数据迁移,平均时间复杂度是 O(n)
    • 链表:找到指定值的时间复杂度为 O(n),删除的时间复杂度为 O(1),平均时间复杂度为 O(n)

如何写出正确的链表代码

理解指针或引用的含义

  • 对于 Java 来说,引用中存储的就是数据的内存地址。
  • 指针可以理解为 Java 中的引用
  • p->next=q
    • 这行代码是说,p 结点中的 next 指针存储了 q 结点的内存地址。

警惕指针丢失和内存泄漏

  • 插入节点时,一定要注意操作顺序,防止节点丢失

利用哨兵简化实现难度

  • 可以利用哨兵,避免很多“边界问题”的特殊处理
  • 很多地方都有用到,例如:插入排序、归并排序、动态规划

重点留意边界条件处理

  • 边界条件处理,决定了程序的健壮性。不管是算法还是日常编程,都要十分注意
    • 如果链表为空时,代码是否能正常工作?
    • 如果链表只包含一个结点时,代码是否能正常工作?
    • 如果链表只包含两个结点时,代码是否能正常工作?
    • 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?

推荐练习

  • 推荐练习
  • 单链表反转
  • 链表中环的检测
  • 两个有序的链表合并
  • 删除链表倒数第 n 个结点
  • 求链表的中间结点

链表翻转

题目

反转一个单链表

示例

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

解答

  1. 最简单的方法是借助 JavaAPI 进行翻转
    1. Collections.reverse(list);
  1. 自己实现思路
    1. 借助节点 prev 和 cur
    2. cur 表示当前正在处理的节点
    3. prev 表示当前处理节点的前一个节点
  2. 第 0 个元素开始翻转

public static class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }
}

static class Solution {
    public ListNode reverseList(ListNode head) {
        //1. 处理特殊情况
        if (head == null || head.next == null) {
            return head;
        }
        //2. 通过 prev 指针进行翻转
        ListNode prev = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode tmp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = tmp;
        }
        return head;
    }
}

回文子串

题目

请判断一个链表是否为回文链表。

示例 1:
    输入: 1->2
    输出: false
示例 2:
    输入: 1->2->2->1
    输出: true

你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解答

image.png

思路

  • 回文子串可以理解为比较 前半段链表 跟 翻转后的后半段链表。所以首先要找到 中间点
  • 可以通过 快/慢 两个指针找到中间点
    • 快指针:每次向后移动两个位置
    • 满指针:每次向后移动一个位置
  • 当快指针达到链表尾部的时候,慢指针则正好到达链表中心
 static class Solution {

        public static boolean isPalindrome(ListNode head) {
            //1. 判断特殊情况
            if (head == null) {
                return true;
            }

            //2. 使用快慢两个指针,找到中间位置
            ListNode slow = head;
            ListNode fast = head;
            while (fast.next != null && fast.next.next != null) {
                slow = slow.next;
                fast = fast.next.next;
            }

            //3. 翻转后半段链表
            ListNode reverse = reverseList(slow.next);
            while (reverse != null) {
                if (reverse.val != head.val) {
                    return false;
                }
                reverse = reverse.next;
                head = head.next;
            }
            return true;
        }

        public static ListNode reverseList(ListNode head) {
            //1. 处理特殊情况
            if (head == null || head.next == null) {
                return head;
            }

            //2. 通过 prev 指针进行翻转
            ListNode prev = null;
            ListNode cur = head;
            while (cur != null) {
                ListNode tmp = cur.next;
                cur.next = prev;
                prev = cur;
                cur = tmp;
            }
            return prev;
        }

        public static void main(String[] args) {
            ListNode head = new ListNode(1);
            ListNode next1 = new ListNode(2);
            ListNode next2 = new ListNode(2);
            ListNode next3 = new ListNode(1);
            head.next = next1;
            next1.next = next2;
            next2.next = next3;
            System.out.println(isPalindrome(head));
            ;
        }
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值