Java数据结构-LinkedList与链表

1. ArrayList****的缺陷

上节课已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元 素:

由于其底层是一段连续空间,当ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后
搬移,时间复杂度为****O(n),效率比较低,因此ArrayList****不适合做任意位置插入和删除比较多的场景。因此:java
集合中又引入了LinkedList,即链表结构。

**2.**链表

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

注意:

1.从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续2.现实中的结点一般都是从堆上申请出来的3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

虽然有这么多的链表的结构,但是我们重点掌握两种:
无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如 哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
这里我们这样定义链表一个节点

3.单链表的实现

java 复制代码
   static class ListNode{
        public int vla;
        public ListNode next;

        public ListNode(int vla){
            this.vla = vla;
        }
    }

链表的打印

java 复制代码
    public void display(){
        ListNode cur = head;
        while(cur!=null)
        {
            System.out.print(cur.vla+" ");
            cur=cur.next;
        }
    }

cur会依次循环下去,依次打印

初始化链表

java 复制代码
   public void Createlist(){
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(1);
        ListNode node3 = new ListNode(1);
        ListNode node4 = new ListNode(1);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        this.head = node1;

    }

头插法

尾插法

java 复制代码
  public void addLast(int data){
        ListNode newnode=new ListNode(data);
        if(head==null){
            head=newnode;
            return;
        }
       else{
            ListNode cur=head;
            while(cur.next!=null){
                cur=cur.next;
            }
            cur.next=newnode;
        }
    }

任意位置插入

java 复制代码
   public void addIndex(int index,int data){
   if(index<0||index>size()){
       throw new IndexOutOfBoundsException();
   }
   
     ListNode newnode=new ListNode(data);
     ListNode cur=head;
     if(index==0){
         addFirst(data);
         return;
     }
     if(index==size()){
         addLast(data);
         return;
     }
     int i=0;
       while(i<index){
           cur=cur.next;
           i++;
       }
       newnode.next=cur.next;
       cur.next=newnode;
    }

删除第一次出现关键字为key的节点

java 复制代码
    public void remove(int key){
   if(head==null){
       return;
   }
   if(head.vla==key){
       head=head.next;
       return;
   }
   ListNode cur=find(key);
   if(cur==null){
       return;
   }
   ListNode del=cur.next;
   cur.next=del.next;
    }

删除所有值为key的节点

java 复制代码
   public void removeAllKey(int key){
        if(head==null){
            return;
        }
   ListNode current = head.next;
   ListNode prev=head;
   while(current!=null){
       if(current.vla==key){
          prev.next=current.next;
          current=current.next;
       }
       else{
           prev=current;
           current=current.next;
       }
       if(head.vla==key){
           head=head.next;
       }
   }

清空链表

java 复制代码
    public void clear(){
   ListNode cur = head;
   while(cur!=null){
       ListNode temp=cur.next;
       cur.next=null;
       cur=temp;
   }
   head=null;
    }

链表反转

java 复制代码
   public ListNode reverseList(ListNode head) {
        if(head==null){
            return head;
        }
        ListNode cur=head.next;
        head.next=null;
        while(cur!=null){
            ListNode prev=cur.next;
            cur.next=head;
            head=cur;
            cur=prev;
        }
        return head;
    }

给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点 使用快慢指针解决

java 复制代码
    public ListNode middleNode(ListNode head) {
             if(head==null){
            return head;
        }
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }

返回倒数第k个值

java 复制代码
   public int kthToLast(ListNode head,int k){
        if(head==null){
            return -1;
        }
        if(k<=0) return -1;
        ListNode fast=head;
        ListNode slow=head;
  int c=0;
  while(c!=(k-1)){
      fast=fast.next;
      if(fast.next==null){
          return -1;
      }
      c++;
  }
  while(fast.next!=null){
      slow=slow.next;
      fast=fast.next;
  }
        return slow.vla;
    }

合并两个有序链表

最开始我们定义一个newH用来保存这个链表头结点

我们的思路是首先定义一个零时变量temp,然后呢比较heada.val和headb.val的大小,

如果headb的val大那我们就将heada赋给temp.next,heada往前走一步即 heada=heada.next

temp往前走一步,即temp=temp.next

如果heada的val值大那我们就将headb赋给temp.next,headb往前走一步即 headb=headb.next

temp往前走一步,即temp=temp.next

循环上面的操作,然后直到链表a或者链表b为空,此时我们还没有结束,temp的next指针还是空,需要把剩下的连接起来

最后返回newH

java 复制代码
    public ListNode mergeTwoLists(ListNode heada, ListNode headb) {
   //合并两个链表
        ListNode p=new ListNode(-1);
        //开始遍历两个链表
        ListNode temp=p;
        while (heada!=null&&headb!=null){
            if(heada.val<headb.val){
                temp.next=heada;
                heada=heada.next;
                temp=temp.next;
            }
            else {
                temp.next=headb;
               temp=temp.next;
               headb=headb.next;
            }
            }
            if(heada!=null){
            temp.next=heada;
            }
            if(headb!=null){
                temp.next=headb;
            }
        return p.next;
    }

. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前

这个题目我们定义两个链表bs,as,同时在定义两个指针be,ae,给定一个值,把原来链表小于这个值x的放到bs,大于这个值x放到as,同时be和ae指向链表尾部

最后遍历完上面的链表以后,我们开始将两个链表连接,返回bs,但是有一点需要注意,我们要判断bs是否为空,如果bs为空,那么所有的元素都比关键值x大,所有的元素放在as里面,只需要返回as,随后不管as是否为空,他的尾部应该置为空

java 复制代码
    public ListNode partition(ListNode pHead, int x) {
        // write code here
        ListNode cur = pHead;
        ListNode bs = null;
        ListNode be = null;
        ListNode as = null;
        ListNode  ae = null;
        while (cur != null) {
            if (cur.val < x) {
                if (bs == null) {
                    bs = be = cur;
                } else {
                    be.next = cur;
                    be = be.next;
                }
                cur = cur.next;
            } else {
                if (as == null) {
                    as = ae = cur;
                } else {
                    ae.next = cur;
                    ae = ae.next;
                }
                cur = cur.next;
            }
        }

        if (bs == null) {
            return as;
        }
          be.next = as;
     if(as!=null){
        ae.next=null;
     }
       return bs;
    }

回文链表

我们的思路是这样的,回文链表分为两种,一种奇数个数链表节点,一种偶数个链表节点,我们首先来看如果是奇数链表,我们首先使用快慢指针找到链表中间节点,然后开始定义一个指针cur=slow.next,,定义一个指针curn=cur.next用来保存下一个节点的地址,从slow往后开始反转链表,将cur.next=slow,slow和cur往后,slow=cur,cur=curn直到curn等于空

java 复制代码
public boolean huiwen(){
     ListNode fast=head;
     ListNode slow=head;
     while(fast.next!=null&&fast!=null){
         slow=slow.next;
         fast=fast.next.next;
     }
     ListNode cur=slow.next;
     while(cur!=null){
         ListNode curn=cur.next;
         cur.next=slow;
         slow=cur;
         cur=curn;
     }
     while(head!=slow){
         if(head.vla!=slow.vla){
             return false;
         }
         if(head.next==slow){
             return true;
         }
         head=head.next;
         slow=slow.next;
     }
     return true;
}

相交链表

我们的思路是先求两个链表的长度,定义两个指针hl=heada,hs=headb,其中长的链表先走他们的差值,随后再让他们一起走,直到他们相交,即hl=hs,然后我们就返回其中一个指针,如果没有相交点,那么就返回null

java 复制代码
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode hl=headA;
        ListNode hs=headB;
        int lena=0;
        int lenb=0;
        while(hl!=null){
            hl=hl.next;
            lena++;
        }
           while(hs!=null){
            hs=hs.next;
            lenb++;
        }
        hl=headA;
        hs=headB;
        int c=lena-lenb;
        if(c<0){
            hl=headB;
            hs=headA;
            c=lenb-lena;
        }
        for(int i=0;i<c;i++){
            hl=hl.next;
        }
        while(hl!=hs){
            hl=hl.next;
            hs=hs.next;
        }
        if(hs==null){
            return null;
        }

        return hl;

    }

判断链表中是否有环

【思路】
快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表 带环则一定会在环中相遇,否则快指针率先走到链表的末尾。比如:陪女朋友到操作跑步减肥。
【扩展问题】
为什么快指针每次走两步,慢指针走一步可以?
假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快 指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离 就缩小一步,不会出现每次刚好是套圈的情况,因此:在慢指针走到一圈之前,快指针肯定是可以追上慢指
针的,即相遇。
快指针一次走3步,走4步,...n步行吗?

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

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

java 复制代码
    public ListNode detectCycle(ListNode head) {
         ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.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 slow;
        
    }

4.什么是LinkedList

LinkedList的底层是双向链表结构(链表后面介绍),由于链表没有将元素存储在连续的空间中,元素存储在单独的节 点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

5.LinkedList****的模拟实现

初始化双链表

java 复制代码
  static  class Node{
        int data;
        Node next;
        Node prev;
        public Node(int data){
            this.data = data;
        }
    }
public Node head;
  public  Node tail;
    // 2、无头双向链表实现

头插入数据

java 复制代码
 public void addFirst(int data){
        if(head==null){
            head=new Node(data);
        }
        Node temp=new Node(data);
       temp.prev=head;
        head.next=temp;
        head=temp;
    }

尾插入数据

java 复制代码
 public void addLast(int data) {
        Node temp=new Node(data);
        if (head == null) {
            head = tail = temp;
        } else {
            tail.next = temp;
            temp.prev = tail;
            tail = tail.next;
        }
    }

指定位置插入

java 复制代码
  //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
    if(index<0||index>size()){
        return;
    }
    if(head==null){
        head=new Node(data);
    }
    if(index==0){
        addFirst(data);
        return;
    }
    if(index==size()){
        addLast(data);
        return;
    }
     Node temp=new Node(data);
     Node cur=head;
     for(int i=0;i<index-1;i++){
         cur=cur.next;
     }
     cur.next=temp;
     temp.prev=cur;


    }

检查是否包含关键字

java 复制代码
//查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
  Node temp=head;
      while(temp!=null){
        if(temp.data==key){
            return true;
        }
      }
      return false;
    }

删除第一次出现key的

java 复制代码
 //删除第一次出现关键字为key的节点
    public void remove(int key){
        Node cur=head;

        while(cur!=null){
            if(cur.data==key){
                if(cur==head){
                    head=head.next;
                    if(head!=null){
                        head.prev=null;
                    }
                }
                else {
                    cur.prev.next = cur.next;
                    if(cur.next==null){
                        tail=tail.prev;
                    }
                    else {
                        cur.prev.next=cur.prev;
                    }
                    return;
                }

            }
            cur=cur.next;
        }
    }

删除所有key值

java 复制代码
  //删除所有值为key的节点
    public void removeAllKey(int key){
        Node cur=head.next;

//        while(cur!=null){
//            if(cur.data==key){
//                if(cur==head){
//                    head=head.next;
//                    if(head!=null){
//                        head.prev=null;
//                    }
//                }
//                else {
//                    cur.prev.next = cur.next;
//                    if(cur.next==null){
//                        tail=tail.prev;
//                    }
//                    else {
//                        cur.next.prev=cur.next;
//                    }
//                }
//
//            }
//            cur=cur.next;
//        }
         while(cur.next!=null){
             if(cur.data==key){
                 cur.prev.next = cur.next;
                 cur.next.prev=cur.next;
             }
             cur=cur.next;
         }
        if(head.data==key){
            head=head.next;
            if(head!=null){
                head.prev=null;
            }
        }
        if(tail.data==key){
            tail=tail.prev;
        }

    }

双链表长度

java 复制代码
//得到单链表的长度
    public int size(){
   Node cur=head;
   int c=0;
    while(cur.next!=null){
        cur=cur.next;
        c++;
    }
    return c;
    }

显示链表

java 复制代码
public void display(){
   Node cur=head;
   while(cur!=null){
       System.out.print(cur.data+" ");
       cur=cur.next;
   }
    }
相关推荐
dot to one1 小时前
B树系列在数据库中的应用
数据结构·数据库·b树
今天你TLE了吗2 小时前
JVM学习笔记:第九章——StringTable字符串常量池
java·jvm·笔记·后端·学习
无尽的罚坐人生2 小时前
hot 100 543. 二叉树的直径
数据结构·算法·leetcode
心前阳光2 小时前
Mirror网络库插件使用4
java·linux·网络·unity·c#·游戏引擎
Rsun045512 小时前
定时任务如何保证任务的可靠性和幂等性?
java
西野.xuan2 小时前
【effective c++】条款四十三:学习处理模版化基类内的名称
java·c++·学习
Nontee2 小时前
Java 后端开发面试技能清单
java·面试
1104.北光c°2 小时前
JVM虚拟机【八股篇】:类加载机制与性能调优
java·开发语言·jvm·笔记·程序人生·调优·双亲委派
轻颂呀2 小时前
排序——堆排序
数据结构·算法