用java模拟实现单链表(含几道例题)、双链表——看不懂点举报❗❗❗❗

链表

单链表

单链表的结构与创建

结构

单链表具有表头,数据和指针(指向下一元素)

代码

java 复制代码
public class SingleLinkList {
    //创建头指针
    private Node head = null;

    //链表格式
    private class Node {
        //存储内容
        int element;
        Node next;
	
        public Node(int element, Node next) {
            this.element = element;
            this.next = next;
        }
    }
}

头插

原理

先创建一个数据块,将链表块的next指针指向head连接的内容,再将head赋值为链表块

代码

java 复制代码
public void addFirst(int element) {
        //创建数据块,先将头指针连接的内容接到插入元素上,再将插入元素赋值给头指针
        head = new Node(element, head);
    	// Node first = head;
    	// head = first
    	  
    }

尾插

原理

尾插的原理比较简单,直接让最后一个元素的next指针指向刚创建的元素,但是链表不是数组,无法从下标直接调用到最后元素,所以只能采取遍历的方法,移动到最后一个元素,再进行赋值。

代码

尾部赋值
java 复制代码
public void addLast(int element) {
        //判断链表是否为空
//        if (head == null) {
//            addFirst(element);
//        } else {//不为空,调用遍历,查找最后一个元素
//            Node last = traversal();
//            last.next = new Node(element, null);
//        }
        Node last = traversalLast();
//		Node last = recursion(head);
        if (last == null) {
            addFirst(element);
            return;
        }
        last.next = new Node(element, null);
    }
for循环寻找Last节点
java 复制代码
private Node traversalLast() {
        if (head == null) {
            return null;
        }
        Node point;
       /*
       这里的写的是point.next != null而不是point != null的原因是:
        point的话即使他是最后一个他也会再往下运行一次,所以此时point指向的是空指针
        */
        for (point = head; point.next != null; point = point.next) {

        }
        return point;
    }
递归寻找Last节点
java 复制代码
private Node recursion(Node node) {
    	//第一个条件判断链表是否为空,第二个判断是否有下一个节点
        if(node == null || node.next == null) {
            return node;
        }
    	//无论递归多少次,最后return的一定是if语句中return的node
         return recursion(node.next);
    }

头删

原理

头删即直接将头节点指向第一节点的next节点,而头节点本就等于第一节点,所以头节点直接指向头节点的next节点

代码

java 复制代码
public void removeFirst() {
        if (head == null) {
            System.out.println("头指针为空!");
            return;
        }
        head = head.next;
    }

查找删除

原理

通过遍历查找待删除的索引的前一个节点,将前一个节点的next值赋值为待删除节点的next值

代码

java 复制代码
public void remove(int index) {
    Node prev = searchIndex(index - 1);
//  Node prev = recursionIndex(index - 1, 0, head);
    Node removed = prev.next;
    prev.next = removed.next;
}
java 复制代码
//递归查找索引
private Node recursionIndex(int index, int i, Node point) {
        if(index == i){
            return point;
        }else if(point == null){
            return null;
        }
        i++;
        return recursionIndex(index,i,point.next);
    }
//for循环查找索引
 private Node searchIndex(int index) {
        int i = 0;
        Node point;
        for (point = head; point != null; point = point.next, i++) {
            if (i == index) {
                //找到了返回目标节点
                return point;
            }
        }
        return null;//没有找到
    }

一遍删除完所有的相同值

思路

创建前后节点prev与rem,判断rem的element与val是否相同,如果相同就让prev.next = rem.next,然后rem = rem.next(节点向后移一位。如果没找到,prev和rem都向后移一位,也就可以写为prev = rem; rem = rem.next;

代码

java 复制代码
public void lastingRemove(int val) {
    Node prev = head;
    Node rem = head.next;
    while (rem != null) {
        if (rem.element == val) {
            prev.next = rem.next;
            rem = rem.next;
        } else {
            prev = rem;
            rem = rem.next;
        }
    }
    if (head.element == val) {
        head = head.next;
    }
}

倒序排列链表

思路

使用三指针,先将new一个新的节点cur,获取head后面的一个节点,然后再创建一个节点p,记录cur之后的节点。这时候将head节点的next置空,然后将cur节点插如到head节点的前面,再将head赋值为cur,成为新的头节点(头插)。

代码

java 复制代码
public void displaceElement() {
    Node cur = head.next;
    head.next = null;
    Node p = cur.next;
    //注意不是p.next否则会漏掉最后一个元素
    while (p != null) {
        p = cur.next;
        cur.next = head;
        head = cur;
        cur = p;
    }
}

快慢指针---输出节点中间数值

思路

创建两个指针,快指针一次位移两个节点,慢指针一次位移一个节点。当快指针到终点的时候,慢指针刚好在链表的中间节点(奇数为中间,偶数为中间的右节点)。

代码

java 复制代码
//快慢指针,输出一个链表的中间值,如果是偶数的话就输出右边的值
public void outPutMiddleNode() {
    Node fast = head;
    Node slow = head;
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    System.out.println(slow.element);
}

快慢指针---输出指定倒数第K个节点的element

思路

通过快慢指针,先将fast指针向后移动k-1个节点,然后再将fast、slow指针同时移动,这样等fast指针结束的时候,slow指针就指在倒数第K个节点上。

代码

java 复制代码
//快慢指针,输出倒数第K个节点
public void outPutBottomKTh(int k) {
    if (k == 0) {
        System.out.println("非法输入");
        return;
    }
    Node fast = head;
    Node slow = head;
    while (k - 1 != 0) {
        fast = fast.next;
        if (fast == null) {
            System.out.println("非法输入");
            return;
        }
        k--;
    }
    while(fast.next != null) {
        slow = slow.next;
        fast = fast.next;
    }
    System.out.println(slow.element);
}

删除倒数第n个节点,并返回链表

思路

删除倒数第n个节点和上题使用的思路是相同的------快慢指针,但是写法有点区别,因为这里如果删除的是头节点的话,还需要特殊处理。所以我们需要创建一个标枪节点。

还是创建双节点fast和slow,让fast = head;slow = mark;这样位移的时候fast和slow中间可以始终差n个节点,当fast == null时,slow.next就是待删除的节点,令slow.next = slow.next.next即可。

此时如果只有一个节点的话,n = 1,这时fast往前走一步就为null,所以slow.next = slow.next.next (null)。

代码

java 复制代码
public Node removeNthFromEnd(int n) {
    Node mark = new Node(0, head);
    Node fast = head;
    Node slow = mark;
    
    while(n != 0) {
        fast = fast.next;
        n--;
    }
    
    while(fast != null) {
        fast = fast.next;
        slow = slow.next;
    }
    
    slow.next = slow.next.next;
    
    return mark.next;
}

链表的合并

思路

创建一个标枪指针newhead,再创建一个移动指针tmp,创建循环将head1和head2的元素拿来对比,将小的赋值给tmp,然后向下移动一个节点,当head1或者head2为空的时候,就说明有一个链表已经插入完毕,只需要将剩下的链表的head直接赋值给tmp.next即可。

​ 图一

如上图,创建一个标枪指针newhead和一个移动指针tmp,下面就是比较head1的第一个Node和head2第一个Node,head1的element比head2的小,所以将head1赋值给tmp

​ 图二

图二将head1赋值给tmp之后,head1 = head1.next; tmp = tmp.next;下面就在进行比较。

​ 图三

图三是相同的,将head2的节点赋值给tmp.next;直到图四执行完毕。

​ 图四

代码

java 复制代码
public void CombineLinkList(SingleLinkList singleLinkList1, SingleLinkList singleLinkList2) {
        Node head1 = singleLinkList1.head;
        Node head2 = singleLinkList2.head;
        if (head1 == null && head2 == null) {
            System.out.println("双链表为空!");
            return;
        }
        Node newhead =new Node(0,null);

        Node tmp = newhead;

        while(head1 != null && head2 != null ) {
            if (head1.element <= head2.element) {
                tmp.next = head1;
                head1 = head1.next;
                tmp = tmp.next;
            } else {
                tmp.next = head2;
                head2 = head2.next;
                tmp = tmp.next;
            }
        }
        if (head1 == null) {
            tmp.next = head2;
        } else {
            tmp.next = head1;
        }
        for (Node p = newhead.next;p != null;p = p.next) {
            System.out.println(p.element);
        }
    }

回文链表

题目及思路

回文链表的结构:

​ 首先找到中间节点,然后从中间节点开始往后的节点全部进行反转

除了中间节点slow,再定义两个节点,一个mid用来记录slow.next节点,一个cur用来记录slow.next.next节点,那么当这两个节点到位了之后,就开始反转,mid.next = slow,如下图

反转完后,需要节点往后走,可以写为slow = mid;mid = cur;然后cur = cur.next;当cur = null时,说明已经遍历完毕。

代码

java 复制代码
public void palindromeLinkedList() {
    Node fast = head;
    Node slow = head;
    //寻找中间节点slow
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    //从slow开始进行反转链表
    Node mid = slow;
    Node cur = slow.next;
    while(cur != null) {
        mid = cur;
        cur = cur.next;
        mid.next = slow;
        slow = mid;
    }
    /*开始检测是否回文,Node pre = head节点从前往后,mid节点从后往前
    这里的条件有两种情况,一种是奇数个数链表,条件是两个指针走到中间,那么地址相同
    一种是偶数个数链表,当pre.next = mid的时候,代表已经相遇
    */
    while(pre != mid) {
        if(head.element != mid.element) {
            System.out.println("该链表不是回文链表");
            return;
        }
        //判断第二个条件,当偶数链表相遇时,直接结束循环
        if(pre.next == mid) {
           break;
        }
        pre = pre.next;
        mid = mid.next;
    }
    System.out.println("该链表是回文链表");
}

给定x排序

题目及思路

首先题目需求是给定一个x,将这个链表小于x的值排序在大于x的值的左边,并且保证链表基本顺序不变

所以我们需要创建两个单链表,将小于x的值放在1链表中,大于x的值放在2链表中。

最后再将两个链表连在一起,这里还有两种情况,如果链表中的所有数都小于x值,或者都大于x值,那么直接返回1链表或者2链表。

代码

java 复制代码
public Node sortNodeList(int x) {
    //创建两个链表,一个链表存储的是小于x的,一个链表是存储大于x的
    Node head1 = null;
    Node cur1 = null;
    Node head2 = null;
    Node cur2 = null;
    //遍历链表
    while(head != null) {
        //小于x的存储于头节点为head1的链表
        if (head.element < x) {
            //如果头节点为空的话,就要先赋值给头节点
            if (head1 == null) {
                head1 = head;
                cur1 = head1;
            } else {//使用尾插的方法存储
                cur1.next = head;
                cur1 = cur1.next;
            }
        } else {//大于x存储于头节点为head2的链表
            if (head2 == null) {
                head2 = head;
                cur2 = head2;
            } else {
                cur2.next = head;
                cur2 = cur2.next;
            }
        }
        head = head.next;
    }
    /*
    如果head1为空,则链表中全是大于x的数,反之head2为空则是都是小于x的数,那么就产生两种情况
    head1为空的话直接返回的是head2,反之则返回head1。
     */

    if (head2 == null ) {
        return head1;
    }
    if (head1 == null) {
        return head2;
    }
    cur1.next = head2;
    cur2.next = null;
    return head1;
}

奇偶链表

原理

​ 图一

​ 图二

​ 图三

代码

java 复制代码
public Node oddEvenList() {
    if(head == null) {
        return null;
    }
    Node evenNubmer = head.next;
    Node even = evenNubmer;
    Node odd = head;
    while(even != null && even.next != null) {
        odd.next = even.next;
        odd = odd.next;
        even.next = odd.next;
        even = even.next;
    }
    odd.next = evenNubmer;
    return head;
}

两个链表的第一个共同节点

解法1:

思路

先计算出两个链表的长度,然后相减就是之间的长度差,让长的链表先走长度差之后,再和短的链表同时遍历,这样就能找到共同节点。

代码

java 复制代码
public void sameNode(Node headA, Node headB) {
        int a = 0;
        int b = 0;
        Node l = headA;
        Node s = headB;

        while(l != null) {
            a += 1;
            l = l.next;
        }
        while(s != null) {
            b += 1;
            s = s.next;
        }

        l = headA;
        s = headB;
        int len = a - b;
        if(len < 0) {
            len = b - a;
            l = headB;
            s = headA;
        }
        while(len != 0) {
            l = l.next;
            len--;
        }
        while(s != l) {
            l = l.next;
            s = s.next;
        }
        System.out.println("第一个共同节点为" + s.element);;
    }

解法2:

思路

双指针:headA链表长度为a,headB链表长度为b,共同节点长度为c。

当A指针从headA链表走,B指针从headB走,当A指针走完headA,再走到headB的共同节点Node时,B指针刚好也走完B走到A的Node节点,

a + (b - c) = b + (a - c)时代表有共同节点,如果没有,那么返回空节点null。

代码

java 复制代码
public void sameNode(Node headA, Node headB) {
    Node A = headA;
    NOde B = headB;
    while(A != B) {
        /*
        判断 A 是否为 null,如果不为 null,则将 A 移动到下一个节点,即 A = A.next;
        如果 A 为 null,则将 A 移动到链表 B 的头节点,即 A = headB。*/
        A = A != null ? A.next : headB;
        B = B != null ? B.next : headA;
    }
    return A;
}

环形链表

题目及思路

判断一个链表中是否有环,类似于数学中的追击问题,如果两个指针相遇(相等)那么就是有环,如果有一个为null那么就没有环。

所以这题用快慢指针很好解决,fast走两步,slow走一步,如果fast先等于null,那么就相当于无环,如果有环的话,fast最多走一圈环能追上slow。

代码

java 复制代码
public void hasCycle() {
    Node fast = head;
    Node slow = head;
    while(fast != null && fast.next != n) {
        fast = fast.next.next;
        slow = slow.next;
        if(fast == slow) {
            return true;
        }
    }
    return false;
}

返回环形链表的入口点

思路

代码

java 复制代码
public Node findCycleEntrance() {
    Node fast = head;
    Node slow = head;
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if(slow == fast) {
            break;
        }
    }
    if(fast == null || fast.next != null) {
        return null;
    }
    slow = head;
    while(slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return fast;
}

双向链表

双向链表的结构与创建头、尾节点

结构

双链表具有元素前驱和后继,与单链表相比,双链表可以向前移动。

代码

java 复制代码
public static class Node {
    int element;
    Node pre;
    Node next;

    public Node(int element, Node pre, Node next) {
        this.element = element;
        this.pre = pre;
        this.next = next;
    }
}
//创建头节点
Node head = null;
Node last = null;

头插

原理

创建一个新的节点等于头节点的pre,然后让头节点往前一位

代码

java 复制代码
public void addFirst(int element) {
        if (head == null) {
            head = new Node(element, null , null);
        } else {
            head.pre = new Node(element, null, head);
            head = head.pre;
        }
    }

尾插

原理

先判断head是否为空,为空直接让head等于新创建的节点,并让最后一个节点last等于head。

如果不为空,就要让last.next等于新的节点,然后last = last.next;

代码

java 复制代码
public void addEnd(int element) {
    if (head == null) {
        head = new Node(element, null, null);
        last = head;
    } else {
        last.next = new Node(element, last, null);
        last = last.next;
    }
}

头删

原理

首先判断链表是否为空,然后让head位移到下一个节点,并将这个节点的前驱置为null。

代码

java 复制代码
public void firstRemove() {
       if (head == null) {
           System.out.println("此表为空!");
       }
       head = head.next;
       head.pre = null;
    }

尾删

原理

首先判断链表是否为空,然后让last位移到前一个节点,并将这个节点的next置为null。

代码

java 复制代码
public void endRemove() {
    if (last == null) {
        System.out.println("此链表为空!");
    }
    last = last.pre;
    last.next = null;
}

查找删除

原理

先判断链表是否为空,不为空判断链表头、尾元素是否与待删除元素相等,相等的话执行头删或者尾删。如果不相等就使用循环遍历,找到之后让这个元素的前一个元素的next值指向后一个元素,后一个元素的pre值指向前一个元素。这样待删除元素就没有节点指向,会被垃圾回收。

​ 图一

如图一,假设待删除节点元素是1,那么这是找到的情况,执行完成如图二

​ 图二

代码

java 复制代码
public void searchRemove(int element) {
    if (head == null) {
        System.out.println("此链表为空!");
        return;
    }
    if (head.element == element) {
        head = head.next;
        head.pre = null;
    } else if (last.element == element) {
        last = last.pre;
        last.next = null;
    } else {
        Node cur = head;
        while (cur != null) {
            if (cur.element == element) {
                cur.pre.next = cur.next;
                cur.next.pre = cur.pre;
            }
            cur = cur.next;
        }
    }
    System.out.println("找无此元素");
}

清空链表

原理

双链表清空和单链表有所不同,双链表因为有前驱后继,所以必须都置空才能全部不引用对方,所以必须遍历置空。

遍历置空就需要创造一个节点专门记录下一个节点。置空完所有节点之后头节点和尾节点也需要再次置空。

代码

java 复制代码
public void clearList() {
    Node cur = head;
    while(cur != null) {
        //创建节点记录下一个节点
        Node nextNode = cur.next;
        cur.next = null;
        cur.pre = null;
        cur = nextNode;
    }
    head = last = null;
}

博主小菜鸡一个,上述全部文章都是俺的笔记,只是来分享一下,欢迎各位一起探讨~~

相关推荐
pianmian11 小时前
python数据结构基础(7)
数据结构·算法
ChoSeitaku4 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程4 小时前
双向链表专题
数据结构
香菜大丸4 小时前
链表的归并排序
数据结构·算法·链表
jrrz08284 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
@小博的博客5 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
泉崎6 小时前
11.7比赛总结
数据结构·算法
你好helloworld7 小时前
滑动窗口最大值
数据结构·算法·leetcode
JSU_曾是此间年少8 小时前
数据结构——线性表与链表
数据结构·c++·算法
sjsjs118 小时前
【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
数据结构·leetcode