Java基础数据结构–链表



文章目录

  • Java基础数据结构--链表
  • 链表
  • 链表的定义:
  • 链表的作用:
  • 单链表
  • 如下图:
  • 链表类的写法思路:
  • 代码实现:
  • 以下为单链表的题:
  • 单链表中一些题的图解:
  • 循环链表
  • 代码以及思想:
  • 双向链表
  • 如下图:
  • 代码以及思想:
  • Github 代码链接:


链表

链表的定义:

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

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

在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的链接

链表有很多种不同的类型:单向链表双向链表以及循环链表

链表的作用:

用来存储逻辑地址连序而物理地址不一定连序的数据存储结构


单链表

如下图:

Java项目数据集 java数据表_单链表、双向链表、循环链表

链表类的写法思路:

1.写链表类,类里写节点类
2.数据域,指针域
3.节点类里,无参构造函数(为头节点准备),有参构造函数(为增加节点准备)
4.头引用,链表类中,无参构造函数里面写生成头结点对象(main中生成链表类对象时,头结点对象就生成了)

代码实现:
public class TestLink {
    class Node {
        int data;
        Node next;

        public Node() {
            this.data = data;
            this.next = null;
        }

        public Node(int val) {
            this.data = val;
            this.next = null;
        }
    }

    public Node getHead() {
        return head;
    }

    private Node head;

    public TestLink() {
        this.head = new Node();
    }
    
    //此处写所有与链表有关的方法
    
}
以下为单链表的题:
package pri.qzz.testlink;

import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;

import java.util.LinkedList;

/**
 * @ClassName TestLink
 * @Description TODO
 * @Author QZZ
 * @Date 2018/11/27 12:31
 * @Title 愿上苍有好生之德
 * @Description:
 *
 * 1.写链表类,类里写节点类
 * 2.数据域,指针域
 * 3.节点类里,无参构造函数(为头节点准备),有参构造函数(为增加节点准备)
 * 4.头引用,链表类中,无参构造函数里面写生成头结点对象(main中生成链表类对象时,头结点对象就生成了)
 *
 *
 * 1.头插法,插入数据
 * 2.尾插法
 * 3.得到单链表长度(数据节点个数)
 * 4.任意的位置插入
 * 5.删除节点
 * 6.删除值为val的节点
 * 7.打印单链表数据
 * 8.返回下标
 * 9.删除单链表当中所有值为val的节点   O(n)
 * 10.对链表进行排序(冒泡)
 * 11.找到倒数第K个节点
 * 12.删除倒数第k个节点
 * 13.创建一个环
 * 14.查找是否有环
 * 15.求环的入口点
 * 16.求环的长度
 * 17.单链表的逆置?(使用头插法逆置)
 * 18.单链表的反转
 * 19.O(1)时间复杂度
 * 20.非递归合并两个有序的递增的单链表
 **/
public class TestLink {
    class Node {
        int data;
        Node next;

        public Node() {
            this.data = data;
            this.next = null;
        }

        public Node(int val) {
            this.data = val;
            this.next = null;
        }
    }

    public Node getHead() {
        return head;
    }

    private Node head;

    public TestLink() {
        this.head = new Node();
    }

    //1.头插法
    public void insertHead(int val) {
        Node cur = new Node(val);
        cur.next = this.head.next;
        this.head.next = cur;
    }

    //2.尾插法
    public void insertTail(int val) {
        Node cur = this.head;
        while (cur.next != null) {//找到最后一个为空的节点
            cur = cur.next;
    }
        Node endNode = new Node(val);//生成新节点
        cur.next = endNode;//和之前找到的后面为空的节点链接
    }

    //3.得到单链表长度(数据节点个数)
    public int getLength() {
        Node cur = this.head;
        int count = 0;
        while (cur.next != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    //4.任意的位置插入
    public void insertPos(int pos, int val) {
        if (pos < 0 || pos > getLength()) {
            return;
        }
        Node cur = this.head;
        for (int i = 0; i < pos; i++) {//找到上一个节点
            cur = cur.next;
        }
        Node newNode = new Node(val);
        newNode.next = cur.next;
        cur.next = newNode;
    }

    //5.删除节点(多种写法,也可以有count代表第几个节点和pos相等进行删除)
    public void deletePos(int pos) {
        if (pos < 0 || pos > getLength()) {
            return;
        }
        Node cur = this.head;
        Node deleteCur = this.head.next;
        for (int i = 0; i < pos; i++) {
            //当前节点的上一个节点
            cur = cur.next;
            deleteCur = cur.next;
        }
        cur.next = deleteCur.next;
    }

    //6.删除值为val的节点
    public void deleteVal(int val) {
        Node cur = this.head.next;
        Node pre = this.head;
        while (cur != null) {
            if (cur.data == val) {
                pre.next = cur.next;
            }
            cur = cur.next;
            pre = pre.next;
        }
    }

    //7.打印单链表数据
    public void print() {
        Node cur = this.head;
        while (cur.next != null) {
            System.out.print(cur.next.data + " ");
            cur = cur.next;
        }
    }
    //7.1打印反转后单链表数据  you wen ti  传入reverHead
    public void contraryPrint(Node reverHead) {
        Node cur = reverHead;
        while (cur.next != null) {
            System.out.println(cur.data);
            cur = cur.next;
        }
    }

    //8.返回下标
    public int findPos(int val) {
        int index = 0;
        Node cur = this.head.next;
        while (cur != null) {
            if (cur.data == val) {
                index++;
                return index;
            }
            cur = cur.next;
        }
        return -1;
    }

    //9.删除单链表当中所有值为val的节点   O(n)
    public void deleteAllVal(int val) {
        Node cur = this.head.next;
        Node pre = this.head;
        while (cur != null) {
            if (cur.data == val) {
                pre.next = cur.next;
                cur = cur.next;
            } else {
                cur = cur.next;
                pre = pre.next;
            }
        }
    }

    //10.对链表进行排序(冒泡)
    public void sortBubble() {
        Node cur;
        Node runByRun;
        int temp;
        for (cur = this.head.next; cur.next != null; cur = cur.next) {
            for (runByRun = this.head.next; runByRun.next != null; runByRun = runByRun.next) {
                if (runByRun.data > runByRun.next.data) {

                    temp = runByRun.data;
                    runByRun.data = runByRun.next.data;
                    runByRun.next.data = temp;
                }
            }
        }
    }

    //11.找到倒数第K个节点
    public int findLastPosVal(int pos) {
        if (pos < 0 || pos > getLength()) {
            throw new UnsupportedOperationException("Pos不合法");
        }
        Node cur = this.head;
        Node pre = this.head;
        while (pos - 1 > 0) {
            cur = cur.next;
            --pos;
        }
        while (cur.next != null) {
            cur = cur.next;
            pre = pre.next;
        }
        return pre.data;
    }

    //12.删除倒数第k个节点
    public void deleteLastPos(int pos) {
        if (pos < 0 || pos > getLength()) {
            throw new UnsupportedOperationException("Pos不合法");
        }
        Node cur = this.head;
        Node pre = this.head;
        while (pos > 0) {
            cur = cur.next;
            --pos;
        }
        while (cur.next != null) {
            cur = cur.next;
            pre = pre.next;
        }
        pre.next = pre.next.next;
    }

    //13.创建一个环
    public void createLoop() {
        Node cur = this.head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = this.head.next.next;
    }

    //14.查找是否有环()快慢指针
    public boolean isLoop() {
        Node fast = this.head;
        Node slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }

    //15.求环的入口点
    public int getEnterNode() {
        Node fast = this.head;
        Node slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) {
                break;
            }
        }
        slow = this.head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast.data;
    }
    //16.求环的长度
    public int getLoopLenth() {
        Node fast = this.head;
        Node slow = this.head;
        boolean flg = false;
        int len = 0;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow && flg == true) {
                break;
            }
            if (slow == fast && flg == false) {
                flg = true;
            }
            if (flg == true) {
                len++;
            }
        }
        return len;
    }
    //17.单链表的逆置?(使用头插法逆置)
    public void reverse() {

        Node cur = this.head.next;
        Node curNode = null;
        this.head.next = null;
        while (cur != null) {
            curNode = cur.next;

            cur.next = this.head.next;
            this.head.next = cur;
            cur = curNode;
        }
    }

    //18.单链表的反转
    public Node contrary() {
        Node cur = this.head;
        Node curNext;
        Node pre = null;
        Node reverHead;
        while (cur != null) {
            curNext = cur.next;
         /*   if (curNext == null) {
                reverHead = cur;
            }*/
            cur.next = pre;//后面指针域指向前面地址
            pre = cur;
            cur = curNext;
        }
        reverHead = pre;
        return reverHead;
    }
    //19.O(1)时间复杂度
    public void deleteNode (Node nodeStart, Node nodeDelete) {
        if (nodeDelete.next != null) {//如果删除的不是尾节点
            Node nodeDeleteNext = nodeDelete.next;
            nodeDelete.data = nodeDeleteNext.data;
            nodeDelete.next = nodeDeleteNext.next;
            nodeDeleteNext = null;
        }else if (nodeStart == nodeDelete) {
            nodeStart = null;
            nodeDelete = null;
        }else {//尾节点
            //尾节点的前驱
            Node cur = nodeStart;
            while (cur.next != nodeDelete) {
                cur = cur.next;
            }
            //cur ==> 尾节点的前驱
            cur.next = null;
            nodeDelete = null;
        }
        return;
    }



    public static boolean isCut(TestLink testLink1,TestLink testLink2) {
        TestLink.Node head1 = testLink1.getHead();//指向长的单链表
        TestLink.Node head2 = testLink2.getHead();//指向短的单链表
        int len1 = testLink1.getLength();
        int len2 = testLink2.getLength();
        int myLen = len1-len2;
        if(myLen < 0) {
            head1 = testLink2.getHead();
            head2 = testLink1.getHead();
            myLen = len2-len1;
        }
        //head1===>指向长的单链表
        for (int i = 0; i < myLen; i++) {
            head1 = head1.next;
        }
        while(head1 != head2 && head1 != null && head2 != null) {
            head1 = head1.next;
            head2 = head2.next;
        }

        if(head1 == head2 && head1 != null && head2 != null) {
            return true;
        }
        return false;
    }

    //20.非递归合并两个有序的递增的单链表
    public static TestLink.Node mergeLink(TestLink link1,TestLink link2){
        TestLink.Node newHead = null;//返回的头结点
        /*TestLink.Entry p1 = link1.getHead().next;
        TestLink.Entry p2 = link2.getHead().next;*/
        TestLink.Node p1 = link1.getHead();
        TestLink.Node p2 = link2.getHead();
        if(p1.next.data < p2.next.data) {
            //newHead = link1.getHead();
            newHead = p1;
        } else {
            //newHead = link2.getHead();
            newHead = p2;
        }
        p1 = p1.next;
        p2 = p2.next;
        TestLink.Node tmpHead = newHead;
        while(p1 != null && p2 != null) {
            if(p1.data < p2.data) {
                tmpHead.next = p1;
                p1 = p1.next;
            } else {
                tmpHead.next = p2;
                p2 = p2.next;
            }
            tmpHead = tmpHead.next;
        }
        if(p1 == null) {
            tmpHead.next = p2;
        }
        if(p2 == null) {
            tmpHead.next = p1;
        }
        return newHead;
    }

    //打印合并后的单链表
    public static void showMerge(TestLink.Node entry){
        TestLink.Node  cur = entry.next;
        while(cur != null){
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
    }
}

单链表中一些题的图解:

1.非递归合并两个有序递增的单链表

Java项目数据集 java数据表_链表_02

2.单链表的反转

Java项目数据集 java数据表_链表_03


循环链表

(上面有循环链表图)

代码以及思想:
package pri.qzz.testlink;

/**
 * @ClassName TestClinkDemo
 * @Description TODO
 * @Author QZZ
 * @Date 2018/12/5 17:08
 * @Title 愿上苍有好生之德
 * @Description: 
 * 循环链表(主要关键在
 *
 * 1、public Clink() {}  中生成了头结点,头结点的next和自己链在一起)
 * 2、以前while() 中不等于null,null换成this.next
 * 3、尾插最后的节点要和头结点链接起来(头插法无所谓和一般链表写法一样)
 **/
class Clink {
    class Node {
        private int data;
        private Node next;
        public Node() {
            this.data = -1;
            this.next = null;
        }
        public Node(int val) {
            this.data = val;
            this.next = null;
        }
    }
    public Node head;

    public Clink() {
        this.head = new Node();
        this.head.next = this.head;
    }

    //头插法
    public void insertHead(int val) {
        Node cur = new Node(val);
        cur.next = this.head.next;
        this.head.next = cur;
    }
    //尾插法
    public void insertTail(int val) {
        Node cur = this.head;
        while (cur.next != this.head) {
            cur = cur.next;
        }
        Node newCur = new Node(val);
        cur.next = newCur;
        newCur.next = this.head;
    }
    //删除所有值为val的节点
    public void deleteAllVal(int val) {
        Node cur = this.head.next;
        Node pre = this.head;
        while (cur != this.head) {
            if (cur.data == val) {
                pre.next = cur.next;
                cur = pre.next;
            }else {
                pre = cur;
                cur = cur.next;
            }
        }
    }
    public void show() {
        Node cur = this.head.next;
        while (cur != this.head) {
            System.out.println(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}
public class TestClinkDemo {
    public static void main(String[] args) {
        Clink Node = new Clink();
        //entry.insertHead(12);
        Node.insertTail(1);
        Node.insertTail(1);
        Node.insertTail(2);
        Node.insertTail(1);
        Node.insertTail(3);

        Node.show();
        Node.deleteAllVal(1);
        Node.show();
    }
}

双向链表

如下图:

Java项目数据集 java数据表_链表_04


代码以及思想:
class Dlink {
    class Node {
        private int data;
        private Node next;
        private Node pre;
        public Node () {
            this.data = -1;
            this.pre = null;
            this.next = null;
        }
        public Node (int val) {
            this.data = val;
            this.pre = null;
            this.next = null;
        }
    }
    private Node head;

    public Dlink () {
        this.head = new Node();
    }

    //头插法
    public void insertHead (int val) {
        Node cur = new Node(val);
        cur.next = this.head.next;
        cur.pre = this.head;
        this.head.next = cur;
        if (cur.next != null) {
            cur.next.pre = cur;
        }
    }
    //尾插法
    public void insertTail (int val) {
        Node cur = this.head;
        while (cur.next != null) {
            cur = cur.next;
        }
        Node newCur = new Node(val);
        cur.next = newCur;
        newCur.pre = cur;
    }
    //删除所有值为val的节点
    public void deleteAllVal (int val) {
        Node cur = this.head.next;
        while (cur != null) {
            if (cur.data == val) {
                cur.pre.next = cur.next;
                if(cur.next != null) {
                    cur.next.pre = cur.pre;
                }
            }
            cur = cur.next;
        }
    }
    //打印
    public void show () {
        Node cur = this.head.next;
        while (cur != null) {
            System.out.println(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}
public class TestDlinkDemo {
    public static void main(String[] args) {
        Dlink dlink = new Dlink();

        /*dlink.insertHead(1);
        dlink.insertHead(2);
        dlink.insertHead(3);
        dlink.insertHead(4);
        dlink.insertHead(5);*/

        dlink.insertTail(1);
        dlink.insertTail(1);
        dlink.insertTail(3);
        dlink.insertTail(4);
        dlink.insertTail(1);

        dlink.show();
        dlink.deleteAllVal(1);
        dlink.show();
    }
}

Github 代码链接:

https://github.com/QzzBL/Qzz_Java/tree/master/Java基础数据结构/链表

2018.12.08/周六
                     by 922