Summary of Singly Linked List Algorithm Question Types

Table of contents

1. Basic concepts    

2. The code realizes the singly linked list (including adding, deleting, modifying and checking)

3. Classic question types in singly linked list

3.1 Reverse Singly Linked List

3.2 Determine whether the singly linked list is a palindrome structure

3.3 Copy a linked list with random pointer nodes

1. Basic concepts    

        A linked list is a chained storage data structure, which uses a group of storage units with arbitrary addresses to store the data structure in the linear list. There are many kinds of linked lists, such as single linked list, double linked list, etc. The design idea of ​​single linked list is the most basic, and the implementation of related algorithms may be a little more complicated than that of double linked list, so this article uses single linked list as an example to explain. The data in the singly-linked list is represented by nodes, and the composition of each node is: element (the image of the data element) + pointer (indicating the storage location of the subsequent element), the element is the storage unit for storing data, and the pointer is to connect each The address data of a node, that is, each node in the singly linked list contains a data field and a next field, as shown in the figure below.

         The schematic diagram of the logical structure of the singly linked list is as follows:

         For the convenience of understanding, the editor summarizes the following points for the singly linked list:

①The linked list is stored in the form of nodes;

②Each node of the single-linked list contains a data field (storing data) and a next field (pointing to the next node), and a double-linked list also contains a pre field (pointing to the previous node);

③Each node is not necessarily stored sequentially, that is, the position of the next node is not necessarily the next address of the node, that is to say, the logic of the data elements in the linked list is orderly, and the data elements are in the physical storage unit. out of order

④ The head node of the linked list is null and the head node is not null. Choose according to the code requirements

The node structure of a singly linked list:

class Node<V>{
    V data;
    Node next;
}

The node structure of the double-linked list has one more pre variable of Node type than the single-linked list:

class Node<V>{
    V data;
    Node next;
    Node pre;
}

2. The code realizes the singly linked list (including adding, deleting, modifying and checking)

        Let's use an example to explain how to write a simple singly linked list with code, and realize the function of addition, deletion, modification and query.


//测试代码
public class SingleLinkedListDemo {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode newHero2 = new HeroNode(2, "卢哥", "火麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
        //不考虑排序的加入方式
        singleLinkedList.add(hero1);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);
        singleLinkedList.add(hero4);
        singleLinkedList.showSingleLinkedList();
        System.out.println("==========================================");
        singleLinkedList.reverseList(singleLinkedList.head);
        singleLinkedList.showSingleLinkedList();

        //考虑排序的加入方式
        /*singleLinkedList.addByOrder(hero3);
        singleLinkedList.addByOrder(hero1);
        singleLinkedList.addByOrder(hero4);
        singleLinkedList.addByOrder(hero2);
        singleLinkedList.showSingleLinkedList();*/

        //修改单链表
        /*System.out.println("==========================================");
        System.out.println("修改后的单链表");
        singleLinkedList.update(newHero2);
        singleLinkedList.showSingleLinkedList();*/

        //删除单链表
        /*System.out.println("==========================================");
        System.out.println("删除后的单链表");
        singleLinkedList.del(2);
        singleLinkedList.showSingleLinkedList();*/
    }
}

//单链表
class SingleLinkedList {
    public SingleLinkedList() {
    }

    HeroNode head = new HeroNode(0, "", "");

    //无顺序添加
    public void add(HeroNode heroNode) {//英雄节点直接添加在单链表的最后,不考虑排序问题
        HeroNode temp = head;
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        temp.next = heroNode;
    }

    //有顺序添加
    public void addByOrder(HeroNode heroNode) {//考虑排序问题,讲添加的英雄节点加在指定位置
        HeroNode temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) {//表示temp已经在链表的最后位置,直接在该temp位置插入
                break;
            }
            if (temp.next.no > heroNode.no) {//表示位置(中间位置)已找到,就在temp处插入
                break;
            }
            if (temp.next.no == heroNode.no) {//表示添加的英雄节点的编号已经在单链表中出现
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            System.out.println("编号存在,添加失败");
        } else {
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    //修改节点信息,根据节点编号no修改
    public void update(HeroNode newHeroNode) {
        if (head.next == null) {
            System.out.println("单链表为空");
            return;//这条语句的作用是直接结束,不执行下面的程序
        }
        HeroNode temp = head.next;
        boolean flag = false;
        while (true) {
            if (temp == null) {
                break;
            }
            if (temp.no == newHeroNode.no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        } else {
            System.out.printf("编号为%d的节点没有找到,不能修改", newHeroNode.no);
        }
    }

    //删除节点
    public void del(int no) {
        HeroNode temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                break;
            }
            if (temp.next.no == no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
        } else {
            System.out.printf("没有找到编号为%d的节点", no);
        }
    }

   
    //显示单链表
    public void showSingleLinkedList() {
        if (head.next == null) {
            System.out.println("单链表是空的");
            return;
        }
        HeroNode temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }
}

//创建一个类HeroNode,用来代表单链表中的各个节点
class HeroNode {
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;

    public HeroNode(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    //为了显示英雄信息,可以不用再写一个show()方法,直接重写toString()方法即可
    //重写之后,直接println一个HeroNode变量时,就会按照重写的格式输出,而不是按照默认格式
    //不重写toString()方法那么系统有自己的默认输出,具体见课本359页
    @Override
    public String toString() {
        return "HeroNode{ no=" + no + ", name='" + name + '\'' + ", " +
                "nickName='" + nickName + '\'' + '}';
    }
}

3. Classic question types in singly linked list

3.1 Reverse Singly Linked List

Ideas:

① First create a new head node object newHead;

②Define two auxiliary variables cur and temp, which are used to traverse the singly linked list, cur is used to point to the current node, and temp is used to record the next node of the current node;

③ Traversing to the current node, take out the node and put it behind the new head node (cur.next=newHead.next; newHead.next=cur means that the taken out nodes must be connected before and after, and the root behind the node must be connected first. Connect the wires together, and then connect the previous wire, the order cannot be changed)

④ Connect the head.next of the original linked list to newHead.next

The code is implemented as follows:

//将单链表反转
    public void reverseList(HeroNode head) {
        if (head.next == null || head.next.next == null) {
            return;
        }
        HeroNode cur = head.next;
        HeroNode temp = null;
        HeroNode newHead = new HeroNode(0, "", "");
        while (cur != null) {
            temp = cur.next;
            cur.next = newHead.next;
            newHead.next = cur;
            cur = temp;
        }
        head.next = newHead.next;
    }

3.2 Determine whether the singly linked list is a palindrome structure

        This question type can be done in three ways:

Method 1 (suitable for the written test, the time complexity of the written test is low, this method has high space complexity, but the code is the easiest): open up a stack space (the characteristics of the stack: first in, last out);

Method 2: Use fast and slow pointers (the starting point is different, the strategy of fast and slow pointers needs to be considered by yourself) + stack;

Method 3 (suitable for interviews, the time complexity of the three methods is similar, but the space complexity of method 3 is the lowest o(1), with only a limited number of variables): only use fast and slow pointers, and do not borrow other data structures.


import java.util.Stack;

public class Question1 {

    //这里的head都是value有值的节点

    //方法1(适合笔试,笔试时间复杂度低就行,这个方法空间复杂度高,但是代码最容易):开辟一块栈空间(栈的特点:先进后出)
    public static boolean isPalindrome1(Node head) {
        Stack<Node> stack = new Stack<>();
        Node cur = head;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        while (head != null) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
    }

    //方法2:使用快慢指针(起点不同,快慢指针的策略需要自己思考)+栈
    public static boolean isPalindrome2(Node head) {
        if (head == null || head.next == null) {
            return true;
        }
        Node right = head.next;
        Node cur = head;
        while (cur.next != null && cur.next.next != null) {
            right = right.next;
            cur = cur.next.next;
        }
        Stack<Node> stack = new Stack<>();
        while (right != null) {
            stack.push(right);
            right = right.next;
        }
        while (!stack.isEmpty()) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
    }

    //方法3(适合面试,3种方法的时间复杂度都差不多,但方法3空间复杂度最低o(1),只有有限几个变量):只使用快慢指针,不借用其他数据结构
    public static boolean isPalindrome3(Node head) {
        if (head == null || head.next == null) {
            return true;
        }

        //n1慢指针,一次一步,n2快指针,一次两步,等走完之后,n1指的位置正好是链表中点位置
        Node n1 = head;
        Node n2 = head;
        while (n2.next != null && n2.next.next != null) {
            n1 = n1.next;
            n2 = n2.next.next;
        }

        //将n1之后的节点逆序,n1.next指向null
        n2 = n1.next;
        n1.next = null;
        Node n3 = null;
        while (n2 != null) {
            n3 = n2.next;
            n2.next = n1;
            n1 = n2;
            n2 = n3;
        }

        //进行比对
        n3 = n1;
        n2 = head;
        boolean res = true;
        while (n1 != null && n2 != null) {
            if (n1.value != n2.value) {
                res = false;
                break;
            }
            n1 = n1.next;
            n2 = n2.next;
        }

        //将后半段链表逆序回来,恢复原样
        n1 = n3.next;
        n3.next = null;
        while (n1 != null) {
            n2 = n1.next;
            n1.next = n3;
            n3 = n1;
            n1 = n2;
        }

        return res;
    }

    //测试代码
    public static void main(String[] args) {
        Node data1 = new Node(6);
        Node data2 = new Node(8);
        Node data3 = new Node(9);
        Node data4 = new Node(28);
        Node data5 = new Node(9);
        Node data6 = new Node(8);
        Node data7 = new Node(6);

        SingleLinkedList singleLinkedList = new SingleLinkedList();

        singleLinkedList.add(data1);
        singleLinkedList.add(data2);
        singleLinkedList.add(data3);
        singleLinkedList.add(data4);
        singleLinkedList.add(data5);
        singleLinkedList.add(data6);
        singleLinkedList.add(data7);

        singleLinkedList.showSingleLinkedList();

        System.out.println(isPalindrome3(data1));
    }
}

class Node {
    int value;
    Node next;

    public Node(int value) {
        this.value = value;
    }

    public Node() {
    }

    //为了显示英雄信息,可以不用再写一个show()方法,直接重写toString()方法即可
    //重写之后,直接println一个HeroNode变量时,就会按照重写的格式输出,而不是按照默认格式
    //不重写toString()方法那么系统有自己的默认输出,具体见课本359页
    @Override
    public String toString() {
        return "Node{ value=" + value + '}';
    }
}

//作用:创建单链表(多个节点连接起来)
class SingleLinkedList {

    public SingleLinkedList() {
    }

    //这里的head是空节点
    Node head = new Node();

    //无顺序添加
    public void add(Node node) {//英雄节点直接添加在单链表的最后,不考虑排序问题
        Node temp = head;
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        temp.next = node;
    }

    //显示单链表
    public void showSingleLinkedList() {
        if (head.next == null) {
            System.out.println("单链表是空的");
            return;
        }
        Node temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }
}

3.3 Copy a linked list with random pointer nodes

特殊节点定义
class Node{
    int value;
    Node next;
    Node rand;//rand指针可能指向链表中任意一个节点,也可能指向null
}

Method ① (suitable for written test): Create a hash table, because the hash table has a key-value pair structure, the original linked list is used as the key, and the copied linked list is placed in the value position;

Method ② (suitable for interviews, without borrowing other data structures, but the code is complicated): copy the first node and place it directly behind the first node, and in front of the second node, in turn, the singly linked list becomes 2n nodes, and then Determine next and rand respectively, and finally separate the original linked list and the copied linked list.

The code implementation of method ① is as follows:


import java.util.HashMap;

public class Question3 {
    public static void main(String[] args) {
        //测试代码自己写
        //先创建一个由随机节点组成的单链表,往单链表中加节点
        //然后在调用copyLinkedList1函数
    }

    //使用哈希表复制单链表,哈希表中存放键值对(java中叫Entry)
    public static RandomNode copyLinkedList1(RandomNode head){
        HashMap<RandomNode,RandomNode> map=new HashMap<>();
        RandomNode cur=head;
        while (cur!=null){
            map.put(cur,new RandomNode(cur.value));
            cur=cur.next;
        }
        cur=head;
        while (cur!=null){
            //cur 老节点
            //map.get(cur) 新节点,也就是复制的节点
            map.get(cur).next=map.get(cur.next);
            map.get(cur).rand=map.get(cur.rand);
            cur=cur.next;
        }
        return map.get(head);
    }
}

class RandomNode{
    int value;
    RandomNode next;
    RandomNode rand;

    public RandomNode() {
    }

    public RandomNode(int value) {
        this.value = value;
    }
}

Guess you like

Origin blog.csdn.net/Mike_honor/article/details/126058818