单链表的学习

单链表的理解
单链表在我们java中是一个重要的数据结构,与数组类似,都是用来储存数据的,但是他是一个个的数据链起来,用一组地址任意的存储单元存放线性表中的数据元素,如下图:在这里插入图片描述
我们自定一个Entry类,里面有两个属性((逻辑地址相连,物理地址不相连)):

int data; //要存放的数据
Entry next; //指向下一个节点地址的next域,相当于指针

自己实现一个单链表的

class TestLink {
    //节点类
    class Entry {
        int data;
        Entry next;
        //头节点
        public Entry() {
            this.data = -1;
            this.next = null;
        }
        //得到实体节点的构造函数
        public Entry(int data) {
            this.data = data;
            this.next = null;
        }
    }
    //标志单链表的头部
    private Entry head = null;
    
    public Entry getHead() {
        return this.head;
    }
    public TestLink() {
        this.head = new Entry();
    }
    public TestLink(Entry newHead) {
        this.head = newHead;
    }

这样一个简单的单链表就定义好了,下面将实现一些简单的操作:

头插法
头插法相当于把你要插入的元素插入到链表的头节点的后面,相当于有数据的节点的第一个位置,他的逻辑上实现是如图:
在这里插入图片描述
代码实现:

/**
     * 头插法
     * @param val :要插入的元素
     */
    public void headInsert(int val) {
        //1、得到一个实体节点
        Entry entry =  new Entry(val);
        //2、串起来
        entry.next = this.head.next;
        this.head.next = entry;
    }

尾插发
插入方式与头插法类似,就是把你要插入的元素插入到链表的末尾,如图:
在这里插入图片描述
代码实现:

/**
     * 尾插发
     * @param val : 要插入的元素
     */
    public void tailInsert(int val) {
        //1、找到尾巴
        Entry cur = this.head;
        while(cur.next != null) {
            cur = cur.next;
        }
        //2、插入
        Entry entry =  new Entry(val);
        cur.next = entry;
    }

插入指定的位置
这也是同样的道理,传入两个参数,一个要插入的位置,一个要插入的元素,先遍历链表找到要插入的位置,然后插入,如图:
在这里插入图片描述
代码实现:

/**
     * 插入的指定的位置(第一位置下标为0)
     * @param val :要插入的元素
     * @param pos :要插入的位置
     */
    publicvoid insertPos(int val,int pos) {
        if(pos < 0 || pos >= getLength()+1) {
            return false;
        }
        Entry cur = this.head;
        for(int i = 0;i <= pos-1;i++) {
            cur = cur.next;
        }
        Entry entry = new Entry(val);
        entry.next = cur.next;
        cur.next = entry;

删除元素
单链表的删除机制与数组不同,他是直接把要删除的元素的节点与前后连接的方式直接删除,如图:
在这里插入图片描述
代码实现:

/**
     * 删除节点值为key的元素
     * @param key 要删除的元素
     */
    public void deleteKeyAll(int key) {
        Entry pre = this.head;
        Entry del = this.head.next;
        while(del != null) {
            if(del.data == key) {
                pre.next = del.next;
                del = del.next;
            }else {
                pre = del;
                del = del.next;
            }
        }
    }

获得单链表的长度

/**
     * 获取单链表的长度
     * @return
     */
    public int getLength() {
        int len = 0;
        Entry cur = this.head.next;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        return len;
    }

打印单链表

public void show() {
        Entry cur = this.head.next;
        while(cur != null) {
            System.out.print(cur.data+" ");
            cur = cur.next;
        }
        System.out.println();
    }

单链表的逆置

public void reversLink() {
        Entry cur = this.head.next;
        Entry curNext = null;
        this.head.next = null;
        while(cur != null) {
            curNext = cur.next;
            cur.next = this.head.next;
            this.head.next = cur;//头插法
            cur = curNext;
        }

求倒数第k个节点的data值
定义两个引用,将第一个先走k-1步,接下来两个同时向后遍历。直到第一个指向最后一个,另一个就指向倒数第k个:

public int lastK(int k) {
        if(k < 0 || k > getLength()) {
            return -1;
        }
        Entry entry1 = this.head;
        Entry entry2 = this.head;
        while(k-1> 0) {
            if(entry2.next != null) {
                entry2 = entry2.next;
                k--;
            }else {
                return -1;
            }
        }
        while(entry2.next != null) {
            entry1 = entry1.next;
            entry2 = entry2.next;
        }
        return entry1.data;
    }

判断一个单链表是否有环,求环入口点,求环的长度
环的形成如图:

在这里插入图片描述
首先,先创建一个环:

public void createLoop() {
        Entry cur = this.head;
        while(cur.next != null) {
            cur = cur.next;
        }
        cur.next = this.head.next.next;
    }

判断是否有环:
创建两个引用,一个走一步,一个走两步,如果两个相遇,就说明有环

 public boolean isLoop() {
        Entry fast = this.head;
        Entry slow = this.head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                return true;
            }
        }
        return false;
    }

求环的入口点:
当第一次相遇后一个引用留在相遇点,另一个引用回到原点。同时出发保持相同速度下次遇见的地方就是环的入口点,在这里插入图片描述

public int enterEntry() {
        Entry fast = this.head;
        Entry slow = this.head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {
                break;
            }
        }
        slow = this.head;
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow.data;
    }

求环的长度:

public int getLoopLen() {
        Entry fast = this.head;
        Entry slow = this.head;
        int len = 0;
        boolean first = false;

        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow && first == true) {
                break;
            }
            if(fast == slow && first == false) {
                first = true;
            }
            if(first == true) {
                len++;
            }
        }
        return len;
    }

判断两个单链表是否相交
将长的链表的引用先走两个链表之差个距离,保证开始走的时候两个引用知道结束走的距离一样

public class TestDemoLink {
    /**
     * 判断两个单链表(长度不一定)是否相交,相交求交点
     * 创建两个单链表
     * @param link1
     * @param link2
     */
    public static void createCutLink(TestLink link1,TestLink link2) {
        TestLink.Entry head1 = link1.getHead();
        TestLink.Entry head2 = link2.getHead();
        head1.next.next.next = head2.next.next;
    }

    /**
     * 判断是否相交 求交点
     * @param link1
     * @param link2
     * @return
     */
    public static TestLink.Entry isCut(TestLink link1,TestLink link2) {
        int len1 = link1.getLength();
        int len2 = link2.getLength();
        TestLink.Entry head1 = link1.getHead();
        TestLink.Entry head2 = link2.getHead();
        int mylen = len1-len2;
        if(mylen < 0) {
            head1 = link2.getHead();
            head2 = link1.getHead();
        }
        while(mylen != 0) {
            head1 = head1.next;
            mylen--;
        }
        while(head1 != head2) {
            head1 = head1.next;
            head2 = head2.next;
        }
        if(head1 == head2) {
            return head1;
        }
        return null;
    }

合并两个单链表

/**
     * 合并两个有序的单链表(递增)
     * @param link1
     * @param link2
     * @return
     */
    public static TestLink mergeLink(TestLink link1,TestLink link2) {
        TestLink.Entry newHead = null;
        TestLink.Entry p1 = link1.getHead().next;
        TestLink.Entry p2 = link2.getHead().next;
        if(link1.getHead().next.data < link2.getHead().next.data) {
            newHead = link1.getHead();
            p1 = link1.getHead().next;
        }else {
            newHead = link2.getHead();
            p2 = link2.getHead().next;
        }
        TestLink.Entry tempHead = newHead;
        while(p1 != null && p2 != null) {
            if(p1.data < p2.data) {
                tempHead.next = p1;
                p1 = p1.next;
            }else {
                tempHead.next = p2;
                p2 = p2.next;
            }
            tempHead = tempHead.next;
        }
        if(p1 != null) {
            tempHead.next = p1;
        }
        if(p2 != null) {
            tempHead.next = p2;
        }
        return new TestLink(newHead);
    }

    /**
     * 打印合并的单链表
     * @param link
     */
    public static void showMerge(TestLink link) {
        link.show();
    }

测试打印结果

public static void main(String[] args) {
        TestLink testLink = new TestLink();
        for(int i = 0;i < 10;i++) {
            //testLink.headInsert(i);//
            testLink.tailInsert(i);
        }
        testLink.show();
        TestLink testLink2 = new TestLink();
        for(int i = 10;i < 30;i++) {
            //testLink2.headInsert(i);//
            testLink2.tailInsert(i);
        }
        testLink2.show();

        showMerge( mergeLink(testLink,testLink2) ) ;

        createCutLink(testLink,testLink2);
        testLink.show();
        testLink2.show();
        TestLink.Entry entry = isCut(testLink,testLink2);
        System.out.println(entry.data);
        testLink.createLoop();
        if(testLink.isLoop()) {
            System.out.println("isLoop");
        }
        int data = testLink.enterEntry();
        System.out.println(data);
        int len = testLink.getLoopLen();
        System.out.println(len);
    }

结果:

C:\java\java7\jdk1.7.0_80\bin\java.exe -javaagent:D:\ideaIU-2018.1.5.win\lib\idea_rt.jar=12791:D:\ideaIU-2018.1.5.win\bin -
0 1 2 3 4 5 6 7 8 9 
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
0 1 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
11
isLoop
1
20

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/weixin_42262357/article/details/83904821