链表及相关练习

一.自己实现一个简单的单链表

public class MySingleList {
    static class Node{
        public int val;//代表节点的数据
        public Node next;//存储下一个节点的地址

        public Node(int val) {
            this.val = val;
        }
    }
    public Node head;//代表当前链表的头节点

    //创建链表
    public void createLink(){
        Node node1=new Node(12);
        Node node2=new Node(12);
        Node node3=new Node(12);
        Node node4=new Node(12);
        Node node5=new Node(12);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        head=node1;
    }

    //遍历链表
    public void display(){
        Node cur=head;
        while(cur!=null){
            System.out.print(cur.val+"  ");
            cur=cur.next;
        }
    }

    //查找单链表中是否包含关键字key
    public boolean contains(int key){
        Node cur=head;
        while(cur!=null){
            //如果是引用类型要重写equals
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }

    //得到链表的长度
    public int size(){
        int count=0;
        Node cur=head;
        while(cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }

    //头插法,O(1)
    public void addFirst(int val){
        Node node=new Node(val);
        node.next=head;
        head=node;
    }

    //尾插法,O(n)
    public void addLast(int val){
        Node node=new Node(val);
        Node cur=head;
        //注意判断head==null的情况,否则会空指针异常
        if(cur==null){
            head=node;
            return;
        }
        while(cur.next!=null){
            cur=cur.next;
        }
        cur.next=node;
    }

    //任意位置插入节点
    public void addIndex(int index,int val){
        checkIndex(index);
        if(index==0){
            addFirst(val);
            return;
        }
        if(index==size()){
            addLast(val);
            return;
        }
        Node node=new Node(val);
        Node cur=findIndexSubOne(index);
        node.next=cur.next;
        cur.next=node;
    }
    //找出插入位置的前一个节点
    private Node findIndexSubOne(int index){
        Node cur=head;
        while(index-1!=0){
            cur=cur.next;
        }
        return cur;
    }
    //检查index位置合法性
    private void checkIndex(int index) {
        if(index<0||index>size()){
            throw new ListIndexOutOfException("index位置不合法");
        }
    }
    //查找key节点的前一个节点
    private Node searchPrev(int key){
        if(head==null){
            return null;
        }
        Node cur=head;
        while(cur.next!=null){
            if(cur.next.val==key){
                return cur;
            }
            cur=cur.next;
        }
        return null;//没有需要删除的节点
    }
    //删除第一个出现的关键字为key的节点
    public void remove(int key){
        if(head==null){
            return;
        }
        if(head.val==key){
            head=head.next;
            return;
        }
        Node cur=searchPrev(key);
        if(cur==null){
            return;
        }
        cur.next=cur.next.next;
    }

    //删除所有值为key的节点
    public void removeAll(int key){
        if(head==null){
            return;
        }
        while (head!=null&&head.val==key){
            head=head.next;
        }
        Node cur=head;
        while(cur!=null&&cur.next!=null){
            if(cur.next.val==key){
                cur.next=cur.next.next;
            }else{
                cur=cur.next;
            }
        }
    }

    //清空链表
    public void clear(){
        head=null;
    }

}

二.相关题目练习
1.删除链表中所有指定值的节点(https://leetcode.cn/problems/remove-linked-list-elements/)。
实例:在这里插入图片描述
思路:
在这里插入图片描述

实现:

 public ListNode removeElements(ListNode head, int val) {
        if(head==null){
            return null;
        }
        ListNode cur=head.next;
        ListNode prev=head;
        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;
    }

2.反转一个单链表(https://leetcode.cn/problems/reverse-linked-list/)
例子:
在这里插入图片描述
思路:
实现:

public ListNode reverseList(ListNode head) {
        if(head==null){
            return null;
        }
        if(head.next==null){
            return head;
        }
        ListNode cur=head.next;
        head.next=null;
        while(cur!=null){
            ListNode curNext=cur.next;
            cur.next=head;
            head=cur;
            cur=curNext;
        }
        return head;
    }

3.返回链表的中间节点(https://leetcode.cn/problems/middle-of-the-linked-list/)
例子:
在这里插入图片描述

思路:
在这里插入图片描述
实现:

  public ListNode middleNode(ListNode head) {
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return slow;
    }

4.返回倒数第k个节点(https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9?tpId=196&tqId=37100&rp=1&ru=/exam/oj&qru=/exam/oj&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26pageSize%3D50%26search%3D%25E9%2593%25BE%25E8%25A1%25A8%25E4%25B8%25AD%25E5%2580%2592%25E6%2595%25B0%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D196&difficulty=undefined&judgeStatus=undefined&tags=&title=%E9%93%BE%E8%A1%A8%E4%B8%AD%E5%80%92%E6%95%B0)
例子:
在这里插入图片描述
思路:
在这里插入图片描述
实现:

public ListNode FindKthToTail (ListNode pHead, int k) {
        if(k<=0||pHead==null){
            return null;
        }
        ListNode fast=pHead;
        ListNode slow=pHead;
        //fast走k-1步
        while(k-1!=0){
            fast=fast.next;
            //fast==null时说明k的值要大于链表的长度
            if(fast==null){
                return null;
            }
            k--;
        }
        while(fast.next!=null){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }

5.合并两个有序链表(https://leetcode.cn/problems/merge-two-sorted-lists/)
例子:
在这里插入图片描述

思路:
在这里插入图片描述
实现:

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1==null&&list2==null){
            return null;
        }
        ListNode newHead=new ListNode();
        ListNode temp=newHead;
        while(list1!=null&&list2!=null){
            if(list1.val<list2.val){
                temp.next=list1;
                list1=list1.next;
                temp=temp.next;
            }else{
                temp.next=list2;
                list2=list2.next;
                temp=temp.next;
            }
        }
        if(list1==null){
            temp.next=list2;
        }
        if(list2==null){
            temp.next=list1;
        }
        return newHead.next;
    }

6.以一个基准值分割一个链表(https://www.nowcoder.com/questionTerminal/0e27e0b064de4eacac178676ef9c9d70)
思路:
在这里插入图片描述

实现:

public ListNode partition(ListNode pHead, int x) {
        ListNode bs=null;
        ListNode be=null;
        ListNode as=null;
        ListNode ae=null;
        ListNode cur=pHead;
        while(cur!=null){
            if(cur.val<x){
                if(bs==null){
                    bs=cur;
                    be=cur;
                }else{
                    be.next=cur;
                    be=be.next;
                }
            }else{
                if(as==null){
                    as=cur;
                    ae=cur;
                }else{
                    ae.next=cur;
                    ae=ae.next;
                }
            }
             cur=cur.next;
        }
        //第一段为空,返回第二段
        if(bs==null){
            return as;
        }
        //第二段为空,返回第一段
        if(as==null){
            return bs;
        }
        be.next=as;
        ae.next=null;
        return bs;
    }

7.链表的回文结构(https://leetcode.cn/problems/palindrome-linked-list/submissions/)
例子:
在这里插入图片描述
思路:
在这里插入图片描述
实现:

 public boolean isPalindrome(ListNode head) {
        if(head==null){
            return true;
        }
        if(head.next==null){
            return true;
        }
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        ListNode cur=slow.next;
        ListNode prev=slow;
        while(cur!=null){
            ListNode curNext=cur.next;
            cur.next=prev;
            prev=cur;
            cur=curNext;
        }
        while(head!=prev){
            if(head.val!=prev.val){
                return false;
            }
            //偶数的情况
            if(head.next==slow){
                return true;
            }
            head=head.next;
            prev=prev.next;
        }
        return true;
    }

8.找出链表的公共节点(https://leetcode.cn/problems/intersection-of-two-linked-lists/)
思路:
在这里插入图片描述

实现:

 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lengthA=0;
        int lengthB=0;
        ListNode pA=headA;
        ListNode pB=headB;
        while(pA!=null){
            lengthA++;
            pA=pA.next;
        }
        while(pB!=null){
            lengthB++;
            pB=pB.next;
        }
        //确保pA是长链表,len>0
        int len=lengthA-lengthB;
        pA=headA;
        pB=headB;
        if(len<0){
            pA=headB;
            pB=headA;
            len=-len;
        }
        while(len!=0){
            pA=pA.next;
            len--;
        }
        while(pA!=pB){
            pA=pA.next;
            pB=pB.next;
        }
        return pA;
    }

9.判断链表是否有环(https://leetcode.cn/problems/linked-list-cycle/submissions/)
思路:快慢指针,slow每次走一步,fast每次走二步,slow==fasst说明有环,否则没环
实现:

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

10.返回链表入环的第一个节点(https://leetcode.cn/problems/linked-list-cycle-ii/)
思路:相遇点与入口点的距离和起始点与入口点的距离相等
在这里插入图片描述

实现:

   public ListNode detectCycle(ListNode head) {
        if(head==null){
            return null;
        }
        ListNode slow=head;
        ListNode fast=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;
        }
        while(fast!=head){
            fast=fast.next;
            head=head.next;
        }
        return fast;
    }

三.模拟一个简单的LinkedList

public class MyLinkedList {
    static class ListNode{
        public int val;
        public ListNode prev;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;
    public ListNode last ;
    //头插,O(1)
    public void firstAdd(int val){
        ListNode node=new ListNode(val);
        if(head==null){
            head=node;
            last = node;
            return;
        }
        node.next=head;
        head.prev=node;
        head=node;
    }
    //尾插,O(1)
    public void lastAdd(int val){
        ListNode node=new ListNode(val);
        if(head==null){
            head=node;
            last=node;
            return;
        }
        last.next=node;
        node.prev=last;
        last=node;
    }
    //任意位置插入
    public void addIndex(int index,int val){
        if(index<0||index>size()){
            return;
        }
        if(index==0){
            firstAdd(val);
            return;
        }
        if (index==size()){
            lastAdd(val);
            return;
        }
        ListNode node =new ListNode(val);
        ListNode cur=head;
        while(index!=0){
            cur=cur.next;
            index--;
        }
        node.next=cur;
        node.prev=cur.prev;
        cur.prev.next=node;
        cur.prev=node;
    }
    //删除链表中值为key的节点
    public void remove(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                if(cur==head){
                    head=head.next;
                    if(head!=null){
                        head.prev=null;
                    }
                    return;
                }
                if(cur==last){
                    last=last.prev;
                    last.next=null;
                    return;
                }
                cur.prev.next=cur.next;
                cur.next.prev=cur.prev;
                return;
            }else{
                cur=cur.next;
            }
        }
    }
    //删除链表中所有值为key的节点
    public void removeAll(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                if(cur==head){
                    head=head.next;
                    if(head!=null){
                        head.prev=null;
                    }
                }else{
                    cur.prev.next=cur.next;
                    if(cur.next!=null){
                        cur.next.prev=cur.prev;
                    }else{
                        last=last.prev;
                    }
                }
            }
                cur=cur.next;
        }
    }
    //打印双向链表
    public void display(){
        ListNode cur=head;
        while(cur!=null){
            System.out.print(cur.val+"  ");
            cur=cur.next;
        }
    }
    //链表长度
    public int size(){
        int len=0;
        ListNode cur=head;
        while(cur!=null){
            len++;
            cur=cur.next;
        }
        return len;
    }
    //链表是否包含key
    public boolean contains(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }
    //链表置空
    public void clear(){
        ListNode cur=head;
        while (cur!=null){
            ListNode curNext=cur.next;
            cur.prev=null;
            cur.next=null;
            cur=curNext;
        }
        head=null;
        last=null;
    }
}
不同点ArrayListLinkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(n)
头插需要移动元素,O(n)只需要修改引用的指向,O(1)
插入空间不够需要扩容没有容量的概念
引用场景元素高效存储+频繁访问任意位置插入和删除元素
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值