数据结构与算法-线性表-链表

链表基础

我画了20张图,终于让女朋友学会了翻转链表-码海
该文章介绍了链表的基础知识,

  • 包括链表的定义和表示
  • 为何要设置哨兵节点(即头结点)
  • 头插法和尾插法
    • 需要注意的是,头插法插入完毕之后数据的顺序和输入的顺序是相反的
  • 程序局部性原理(重点关注)
//尾插法
public class LinkedList {
    int length = 0; // 链表长度,非必须,可不加
    Node head = new Node(0); // 哨兵结点
    public void addNode(int val) {
        Node tmp = head;
        while (tmp.next != null) {
            tmp = tmp.next;//从头节点开始顺序向后寻找,直到找到尾结点
        }
        tmp.next = new Node(val);//尾部插入
    }
//头插法,公众号链接有动图
public class LinkedList {
    int length = 0; // 链表长度,非必须,可不加
    Node head = new Node(0); // 哨兵节点
    
     // 头插法
    public void headInsert(int val) {
        // 1.构造新结点
        Node newNode = new Node(val);
        // 2.新结点指向头结点之后的结点
        newNode.next = head.next;
        // 3.头结点指向新结点
        head.next = newNode;
    }

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        int[] arr = {1,2,3,4};
        // 头插法构造链表
        for (int i = 0; i < arr.length; i++) {
            linkedList.headInsert(arr[i]);
        }
        // 打印链表,将打印 4-->3-->2-->1
        linkedList.printList();
    }
}

链表翻转

递归方式翻转链表

在这里插入图片描述

/**
 * 递归翻转结点 node 开始的链表
 */
public Node invertLinkedList(Node node) {
    if (node.next == null) {
        return node;
    }

    // 步骤 1: 先翻转 node 之后的链表
    Node newHead = invertLinkedList(node.next);
    

    // 步骤 2: 翻转该节点,node 的后继节点设置为空的目的是防止形成环
    //e.g.对于当前node为倒数第二个节点时,将下一个节点(即最后一个节点)的后继改为当前节点,但此时有环,所以还要将当前节点的后继
    node.next.next = node;
    node.next = null;

    // 步骤 3: 返回翻转后的头结点,在递归结束时返回的是第二个节点
    return newHead;
}

public static void main(String[] args) {
    LinkedList linkedList = new LinkedList();
    int[] arr = {4,3,2,1};
    for (int i = 0; i < arr.length; i++) {
        linkedList.addNode(arr[i]);
    }
    Node newHead = linkedList.invertLinkedList(linkedList.head.next);
    // 翻转后别忘了设置头结点的后继结点!
    linkedList.head.next = newHead;
    linkedList.printList();      // 打印 1,2,3,4
}

复杂度:由于递归调用了 n 次 invertLinkedList 函数,所以时间复杂度显然是 O(n)

迭代方式翻转链表

复杂度:用迭代的思路来做由于循环了 n 次,显然时间复杂度为 O(n),另外由于没有额外的空间使用,也未像递归那样调用递归函数不断压栈,所以空间复杂度是 O(1),对比递归,显然应该使用迭代的方式来处理

/**
     * 迭代翻转
     */
    public void iterationInvertLinkedList() {
        // 步骤 1
        Node pre = head.next;
        Node cur = pre.next;
        pre.next = null; // pre 是头结点,避免翻转链表后形成环

        while (cur != null) {
            /**
             * 务必注意:在 cur 指向 pre 之前一定要先保留 cur 的后继结点,不然 cur 指向 pre 后就再也找不到后继结点了
             * 也就无法对 cur 后继之后的结点进行翻转了
             */
            Node next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        // 此时 pre 为头结点的后继结点
        head.next = pre;
    }
发布了9 篇原创文章 · 获赞 0 · 访问量 180

猜你喜欢

转载自blog.csdn.net/qq_36629741/article/details/104000693