Java笔记:单链表
发布日期:2021-05-08 06:37:22 浏览次数:9 分类:原创文章

本文共 24933 字,大约阅读时间需要 83 分钟。

单链表

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。

链表的结构有很多,以下情况组合起来就有8种链表结构:

  • 单向,双向
  • 带头,不带头
  • 循环,非循环

我们只需掌握两种结构:
(1)无头单向非循环链表
在这里插入图片描述

(2)无头双向链表:LinkedList的底层实现,双向链表都有一个prev和next, 链表最开始的部分都有一个fiest和last 指向第一个元素和最后一个元素。
在这里插入图片描述
链接:

注意:

  • 不支持随机访问
  • 任意位置插入删除时间复杂度为O(1)

题目列表


编程题:

[1]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode reverseList(ListNode head) {           ListNode newHead=null;        ListNode prev=null;        ListNode cur = head;        while(cur!=null){               ListNode curNext = cur.next;            if(curNext==null){                   newHead=cur;            }            cur.next=prev;            prev=cur;            cur=curNext;        }        return newHead;    }}
[2]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode deleteNode(ListNode head, int val) {           if(head==null){               return null;        }        if(head.val==val){               return head = head.next;        }        ListNode prev = search(head,val);        if(prev==null){               return null;        }else{               prev.next = prev.next.next;        }        return head;    }    public ListNode search(ListNode head,int val){           ListNode cur=head;        while(cur.next!=null){               if(cur.next.val==val){                 return cur;            }            cur=cur.next;        }        return null;           }}
[3]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode removeElements(ListNode head, int val) {           if(head==null){               return null;        }        ListNode prev = head;        ListNode cur = head.next;        while(cur!=null){               if(cur.val == val){                   prev.next = cur.next;                cur = cur.next;            }else{                   prev = cur;                cur = cur.next;            }        }        if(head.val ==val){               head = head.next;        }        return head;    }}
[4]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode middleNode(ListNode head) {           ListNode fast = head;        ListNode low =head;        while(fast!=null&&fast.next!=null){               fast= fast.next.next;            low = low.next;        }        return low;    }}
[5]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode getKthFromEnd(ListNode head, int k) {           if(k<=0){               return null;        }        ListNode fast = head;        ListNode low = head;        while(k-1>0){               if(fast==null){                   return null;            }            fast = fast.next;            k--;        }        while(fast!=null && fast.next!=null){               fast = fast.next;            low = low.next;        }        return low;    }}
[6]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode partition(ListNode head, int x) {           if(head==null){               return null;        }        ListNode as=null;        ListNode ae=null;        ListNode bs=null;        ListNode be=null;        ListNode cur = head;        while(cur!=null){               if(cur.val<x){                   if(as==null){                       as = cur;                    ae = cur;                }else{                       ae.next = cur;                    ae = ae.next;                }            }else{                   if(bs == null){                       bs = cur;                    be = cur;                }else{                       be.next = cur;                    be = be.next;                }            }            cur = cur.next;        }        if(bs==null){               return as;        }        if(as==null){               return bs;        }        ae.next = bs;        be.next = null;        return as;    }}
[7]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {       public ListNode deleteDuplicates(ListNode head) {           if(head ==null){               return null;        }        ListNode newHead = new ListNode(-1);        ListNode tmp = newHead;        ListNode cur = head;        while(cur!=null){               if(cur.next!=null && cur.val == cur.next.val){                   while(cur.next!=null && cur.val == cur.next.val){                       cur =cur.next;                }                cur = cur.next;            }else{                   tmp.next = cur;                tmp = tmp.next;                cur = cur.next;            }        }        tmp.next = null;        return newHead.next;    }}
[8]

涉及到了寻找中间节点及反转链表。

import java.util.*;/*public class ListNode {    int val;    ListNode next = null;    ListNode(int val) {        this.val = val;    }}*/public class PalindromeList {       public boolean chkPalindrome(ListNode A) {           // write code here        if(A==null){               return true;        }        //寻找中间节点        ListNode fast = A;        ListNode low = A;        while(fast!=null && fast.next!=null){               fast = fast.next.next;            low = low.next;        }        //反转链表        ListNode prev = low;        ListNode cur = low.next;        while(cur!=null){               ListNode curNext = cur.next;            cur.next = prev;            prev = cur;            cur = curNext;        }        //判断回文        //节点个数为奇数和偶数时的情况 prev!=A && A.next!=prev        while(prev!=A && A.next!=prev){               if(prev.val!=A.val){                   return false;            }            prev = prev.next;            A = A.next;        }        return true;             }}
[9]

如果有环的话,fast和low一定会相交。

/** * Definition for singly-linked list. * class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { *         val = x; *         next = null; *     } * } */public class Solution {       public boolean hasCycle(ListNode head) {           if(head==null){               return false;        }        ListNode fast = head;        ListNode low = head;        while(fast!=null && fast.next!=null){               fast = fast.next.next;            low = low.next;            if(fast==low){                   return true;            }        }        return false;    }}
[10]

通过fast.next.next和low.next第一次相遇之后,让low回到头节点,再通过low.next和fast.next,若再次相遇,则相遇点即为入环节点。

/** * Definition for singly-linked list. * class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { *         val = x; *         next = null; *     } * } */public class Solution {       public ListNode detectCycle(ListNode head) {           if(head==null){               return null;        }        ListNode fast = head;        ListNode low = head;        while(fast!=null && fast.next!=null){               fast = fast.next.next;            low = low.next;            if(fast == low){                   break;            }        }        if(fast==null || fast.next==null){               return null;        }        low =head;        while(fast!=low){               fast = fast.next;            low = low.next;        }        return low;            }}
[11]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode() {} *     ListNode(int val) { this.val = val; } *     ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */class Solution {       public ListNode mergeTwoLists(ListNode l1, ListNode l2) {           ListNode newHead = new ListNode(-1);        ListNode tmp = newHead;        while(l1!=null && l2!=null){               if(l1.val<=l2.val){                   tmp.next = l1;                tmp = tmp.next;                l1 = l1.next;            }else{                    tmp.next = l2;                tmp = tmp.next;                l2 = l2.next;            }        }        if(l1!=null){               tmp.next = l1;        }        if(l2!=null){               tmp.next = l2;        }        return newHead.next;    }}
[12]
/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { *         val = x; *         next = null; *     } * } */public class Solution {       public ListNode getIntersectionNode(ListNode headA, ListNode headB) {           ListNode l1 = headA;        ListNode l2 = headB;        int lenA=0;        int lenB=0;        while(l1!=null){               lenA++;            l1 = l1.next;        }        while(l2!=null){               lenB++;            l2 = l2.next;        }        int len = lenA-lenB;        l1 = headA;        l2 = headB;        if(len<0){               l1=headB;            l2=headA;            len=-len;        }        while(len>0){               l1 = l1.next;            len--;        }        while(l1!=null && l2!=null){               if(l1==l2){                   return l1;            }            l1 = l1.next;            l2 = l2.next;        }        return null;    }}
[13]

用hashMap来解决比较容易一些。
详细内容可参考博客:

/*// Definition for a Node.class Node {    int val;    Node next;    Node random;    public Node(int val) {        this.val = val;        this.next = null;        this.random = null;    }}*/class Solution {       public Node copyRandomList(Node head) {           HashMap<Node,Node> map = new HashMap<>();        Node cur = head;        while(cur!=null){               Node node = new Node(cur.val);            map.put(cur,node);            cur = cur.next;        }        cur = head;        while(cur!=null){               map.get(cur).next = map.get(cur.next);            map.get(cur).random = map.get(cur.random);            cur = cur.next;        }        return map.get(head);    }}

整体代码应用举例:

// 借助链表中的引用次序进行存储 //需要自己定义一个节点类进行存储//节点类class ListNode {       public int data;    public ListNode next;    public ListNode(int data) {           this.data = data;        this.next = null;    }}class MySingleList {       public ListNode head;//标志头    public MySingleList() {           this.head = null;    }    1 头插法    public void addFirst(int data){           ListNode node = new ListNode(data);        if(this.head == null) {               this.head = node;        }else {               node.next = this.head;            this.head = node;        }    }    2 尾插法    public void addLast(int data) {           ListNode node = new ListNode(data);        ListNode cur = this.head;        //0、判断是否是第一次插入        if(this.head == null) {               this.head = node;        }else {               //1、找尾巴            while (cur.next != null) {                   cur = cur.next;            }            //2、进行插入            cur.next = node;        }    }    /**     * 找到index-1位置的节点  返回当前节点的引用     * @param index     * @return     */	 //找前驱    private ListNode searchIndex(int index) {           //prev-->index-1;        ListNode prev = this.head;        int count = 0;        while (count < index-1) {               prev = prev.next;            count++;        }        return prev;    }        3 插入到index位置    //任意位置插入,第一个数据节点为0号下标    public boolean addIndex(int index,int data){           //下标不合法        if(index < 0 || index > getLength()) {               return false;        }        //头插法        if(index == 0) {               addFirst(data);            return true;        }        ListNode prev = searchIndex(index);        ListNode node = new ListNode(data);        node.next = prev.next;        prev.next = node;        return false;    }		4 返回链表长度    public int getLength() {           int count = 0;        ListNode cur = this.head;        while (cur != null) {               count++;            cur = cur.next;        }        return count;    }    5 打印单链表数据    public void display(){           if(this.head == null) {               return;        }        ListNode cur = this.head;        while (cur != null) {               System.out.print(cur.data+" ");            cur = cur.next;        }        System.out.println();    }   6 查找是否包含关键字key是否在单链表当中   public boolean contains1(int key){           ListNode cur = this.head;        while (cur != null) {               if(cur.data == key) {                   return true;            }            cur = cur.next;        }        return false;    }        public ListNode contains2(int key){           ListNode cur = this.head;        while (cur != null) {               if(cur.data == key) {                   return cur;            }            cur = cur.next;        }        return null;    }       //返回对应值节点的前驱    private ListNode searchPrev(int key) {           ListNode prev = this.head;        while (prev.next != null) {               if(prev.next.data == key) {                   return prev;            }            prev = prev.next;        }        return null;    }        7 删除第一次出现关键字为key的节点    public void remove(int key){           //1、删除的节点如果是头结点        if(this.head.data == key) {               this.head = this.head.next;            return;        }        //2、找到删除的节点的前驱  如果找不到  返回null        ListNode prev = searchPrev(key);        if(prev == null) {               System.out.println("没有你要删除的节点");            return;        }        ListNode del = prev.next;        //3、进行删除        prev.next = del.next;    }    8 删除所有值为key的节点    public void removeAllKey(int key){           ListNode prev = this.head;        ListNode cur = this.head.next;        while (cur != null) {               if(cur.data == key) {                   prev.next = cur.next;                cur = cur.next;            }else {                   prev = cur;                cur = cur.next;            }        }        if(this.head.data == key) {               this.head = this.head.next;        }    }        9 清空链表    public void clear(){           //this.head = null;        while (this.head.next != null) {               ListNode cur = this.head.next;            this.head.next = cur.next;        }        this.head = null;    }        10 反转单链表  时间复杂度为O(N),只遍历了一遍单链表       public ListNode reverseList() {           ListNode prev = null;        ListNode cur = this.head;        ListNode newHead = null;        while (cur != null) {               ListNode curNext = cur.next;            if(curNext == null) {                   newHead = cur;            }            cur.next = prev;            prev = cur;            cur = curNext;        }        return newHead;    }		11 打印反转后的单链表,要重写display    public void display2(ListNode newHead){           if(newHead == null) {               return;        }        ListNode cur = newHead;        while (cur != null) {               System.out.print(cur.data+" ");            cur = cur.next;        }        System.out.println();    }		12 返回中间节点    public ListNode middleNode() {           ListNode low = this.head;        ListNode fast = this.head;        while (fast != null && fast.next != null){      //            fast = fast.next.next;  //当fast遍历完时,low正好位于中间位置,因low比fast慢一半            low = low.next;        }        return low;    }    	13 返回倒数第K个节点    public ListNode findKthToTail(int k) {           if(k <= 0) {               return null;        }        ListNode fast = this.head;        ListNode slow = this.head;        while (k-1 > 0) {               if(fast.next != null) {     //加约束达成遍历一遍链表的目的                fast = fast.next;                k--;            }else {                   System.out.println("没有这个节点");                return null;            }        }        while (fast.next != null) {               fast = fast.next;            slow = slow.next;        }        return slow;    }	    14 以x为基准分割链表    小于x的放在左边,大于等于x的都放在右边    public ListNode partition(int x){           ListNode bs = null;        ListNode be = null;        ListNode as = null;        ListNode ae = null;        ListNode cur = this.head;        while (cur != null) {               if(cur.data < x) {                   //是不是第一次插入                if(bs == null) {                       bs = cur;                    be = bs;                }else {                       be.next = cur;                    be = be.next;   //bs.be 区间的端点                 }            }else {                   //是不是第一次插入                if(as == null) {                       as = cur;                    ae = cur;                }else {                       ae.next = cur;                    ae = ae.next;                }            }            cur = cur.next;        }        //第一个区间没有数据        if(bs == null) {               return as;        }        be.next = as;//若as为空或as正常有值        if(as != null) {               ae.next = null;//as不为空需将结尾置为null,没有此句会出现死循环        }        return bs;  //返回头节点    }	15 删除所有重复节点!!!!!!加入了一个辅助节点	(排序链表,只保留没有重复出现的节点)    public ListNode deleteDuplication(){           if(this.head == null) {               return null;        }        ListNode cur = this.head;        ListNode newHead = new ListNode(-1); //虚拟节点        ListNode tmp = newHead;        while (cur != null) {               //重复的节点            if(cur.next != null             && cur.data == cur.next.data) {                   //每一次都需要判断cur.next                while (cur.next != null                 &&cur.data == cur.next.data) {                       cur = cur.next;                }                cur = cur.next;            }else {                   tmp.next = cur;                tmp = tmp.next;                cur = cur.next;            }        }        //如果最后的节点是重复的,已经删除        // 那么就需要将tmp.next = null;        tmp.next = null;        return newHead.next;    }		16 判断一个链表是否为回文结构	先找到中间节点,然后再反转后半部分,最后前后依次进行比较。	    public boolean chkPalindrome() {   		//两种特殊情况		//没有节点        if(this.head == null) {               return false;        }		//只有一个节点        if(this.head.next == null) {               return true;        }        //1、找到单链表的中间节点        ListNode fast = this.head;        ListNode slow = this.head;        while (fast != null && fast.next != null) {               fast = fast.next.next;            slow = slow.next;        }		//循环结束后,slow即为中间节点		        //2、反转单链表(反转中间节点的后半部分)        ListNode cur = slow.next;        while (cur != null) {               ListNode curNext = cur.next;            cur.next = slow;            slow = cur;            cur = curNext;        }        //3、fast/slow往前    head往后走 走到中间时会出现重合        while (slow != this.head) {   			//            if(slow.data != this.head.data) {                   return false;            }			//增加判断偶数的情况              if(this.head.next == slow) {                   return true;            }            slow = slow.next;            this.head = this.head.next;        }        return true;    }	17 创造一个环    public void creteLoop() {           ListNode cur = this.head;        while (cur.next != null) {               cur = cur.next;        }        cur.next = this.head.next.next;    }		18 判断一个链表是否有环    public boolean hasCycle(){           ListNode fast = this.head;        ListNode slow = this.head;        while (fast != null && fast.next!= null) {               fast = fast.next.next;            slow = slow.next;            if(slow == fast) {                   return true;            }        }        return false;    }		19 返回入环节点	(相遇之后,slow从头节点开始走,fast从相遇处开始走,	当再次相遇时即为入环节点。)    public ListNode detectCycle() {           ListNode fast = this.head;        ListNode slow = this.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 = this.head;        while (slow != fast) {               slow = slow.next;            fast = fast.next;        }        return slow;    }}

测试:

public class TestSingleList {   	20 将两个有序单链表 合并称为一个新的有序链表 并返回	 public static ListNode  mergeTwoLists(ListNode headA, ListNode headB){           ListNode newHead = new ListNode(-1);          ListNode tmp = newHead;        while (headA != null && headB != null) {               if(headA.data < headB.data) {                   tmp.next = headA;                headA = headA.next;                tmp = tmp.next;            }else {                   tmp.next = headB;                headB = headB.next;                tmp = tmp.next;            }        }        if (headA != null) {                tmp.next = headA;        }        if(headB != null) {                tmp.next = headB;        }        return newHead.next;    }    21 判断两个单链表是否有交点	 public static ListNode getIntersectionNode            (ListNode headA, ListNode headB) {           if(headA == null || headB == null) {               return null;        }        ListNode pL = headA;//永远指向长的单链表        ListNode pS = headB;//永远指向短的单链表        int lenA = 0;        int lenB = 0;         //求的lenA  lenB        while (pL != null) {               lenA++;            pL = pL.next;        }        while (pS != null) {               lenB++;            pS = pS.next;        }         //复位        pL = headA;        pS = headB;         //差值-》最长的单链表先走len步        int len = lenA-lenB;        if(len < 0) {               pL = headB;            pS = headA;            len = lenB-lenA;        }         //让pL先走len步        while (len > 0) {               pL = pL.next;            len--;        }         //开始一起走  (pL  != pS ) {一人一步走}        while (pL != pS) {               pS = pS.next;            pL = pL.next;        }        if(pL == null) {               return null;        }        return pL;    }    //创建一个有交点的,单链表    public static void createCut            (ListNode headA, ListNode headB) {           headA.next = headB.next.next;    }    22 复制带随机指针的链表   在OJ上做,OJ提供的ListNode有三个域    public ListNode copyRandomList(Node head) {         if(head == null){             return null;      }     //1、老新进行进行交替链接      ListNode cur = head;      while(cur != null) {             ListNode node = new ListNode(cur.val,cur.next,null);          Node tmp = cur.next;          cur.next = node;          cur = tmp;      }      // 2、修改random      cur = head;      while(cur != null) {             if(cur.random != null) {                 cur.next.random = cur.random.next;              cur = cur.next.next;          }else{               cur = cur.next.next;          }      }     //3、将老新节点 打开      cur = head;      ListNode newHead = cur.next;      while(cur.next != null) {             ListNode tmp = cur.next;          cur.next = tmp.next;          cur = tmp;      }      return newHead;    }}	    public static void main(String[] args) {           MySingleList mySingleList = new MySingleList();        mySingleList.addIndex(0,199);        mySingleList.addLast(1);        mySingleList.addLast(2);        mySingleList.addLast(3);        mySingleList.addLast(4);        mySingleList.addLast(5);        mySingleList.display();        //逆置		System.out.println("逆置");		ListNode newhead = mySingleList.reverseList();		mySingleList.display2(newhead);        //返回中间节点		ListNode node = mySingleList.middleNode();		System.out.println(node.data);        //返回倒数第K个节点		ListNode node1 = mySingleList.findKthToTail(1);		System.out.println(node1.data);        //以x为基准划分区间		ListNode head = mySingleList.partition(3);        //划分区间后,有一个新的头,需要调用display2		mySingleList.display2(head);        //删除重复节点		ListNode newhead1 = mySingleList.deleteDuplication();		mySingleList.display2(newhead1);        //判断是否为回文结构		boolean flg = mySingleList.chkPalindrome();		System.out.println(flg);        //创造一个环		mySingleList.creteLoop();        //判断是否为环结构		boolean flg1 = mySingleList.hasCycle();		System.out.println(flg1);        //连接两个链表		MySingleList mySingleList1 = new MySingleList();		mySingleList1.addLast(3);		mySingleList1.addLast(3);		mySingleList1.addLast(4);		mySingleList1.addLast(5);		mySingleList1.addLast(6);		mySingleList1.addLast(7);		mySingleList1.display();				MySingleList mySingleList2 = new MySingleList();		mySingleList2.addLast(1);		mySingleList2.addLast(3);		mySingleList2.addLast(2);		mySingleList2.addLast(3);		mySingleList2.addLast(6);		mySingleList2.addLast(8);		mySingleList2.display();		ListNode newhead2 = mergeTwoLists(mySingleList1.head,mySingleList2.head);		mySingleList.display2(newhead2);        //判断连个链表是否相交        //创建一个有交点的单链表		 createCut(mySingleList1.head,                mySingleList2.head);				 ListNode node2 = getIntersectionNode(mySingleList1.head,                mySingleList2.head);        System.out.println(node2.data);		    }}
上一篇:Java笔记:双向链表
下一篇:Java时间复杂度和空间复杂度

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2025年03月24日 01时05分03秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章