10道题带你走近JavaSE 链表(附做题链接及注释)

目录

1.线性表

2.顺序表

2.1概念及结构

2.2接口实现

2.3 顺序表的问题及思考

3.链表

3.1链表的概念及结构

3.1.1单链表、双向链表 

3.1.2不带头单链表、带头链表

3.1.3单链表、循环单链表

 3.2 链表的实现

//3.2.1 无表头非循环链表实现

 //3.2.1无头双向链表实现

 3.3oj

1.删除链表中等于给定值“val”的所有节点

2.反转一个单链表

3.给定一个带有头节点head的非空单链表,返回链表的中间节点,。如果有两个中间结点,则返回第二个中间节点。

4.输入一个链表,输出该链表中倒数第k个数

5.将两个有序链表合并为一个新的有序链表并返回,新链表是通拼接给定的两个链表的所有节点组成

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

7.在一个排序的链表中,存在重复的节点,请删除该链表中重复节点,重复的节点不保留,返回链表头指针

8.链表的回文结构

9.输入两个链表,找出它们的第一个公共节点

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


先简述一些关于概念上的

1.线性表

线性表 linear list n 个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见
的线性表:顺序表、链表、栈、队列、字符串 ...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储
时,通常以数组和链式结构的形式存储。

2.顺序表

2.1概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,,在数组上完成数据的增删查改。

静态顺序表:使用定长数组存储

动态顺序表:使用动态开辟的数组存储

静态顺序表是用于确定知道需要存多少数据的场景

静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。

相比之下动态顺序表更灵活,根据需要动态地分配空间大小

2.2接口实现

我们来实现一个动态顺序表,一下是需要支持的接口

public class SeqList {
    // 打印顺序表
    public void display () {   }
    // pos 位置新增元素
    public void add ( int pos , int data ) { }
    // 判定是否包含某个元素
    public boolean contains ( int toFind ) { return true ; }
    // 查找某个元素对应的位置
    public int search ( int toFind ) { return - 1 ; }
    // 获取 pos 位置的元素
    public int getPos ( int pos ) { return - 1 ; }
    // pos 位置的元素设为 value
    public void setPos ( int pos , int value ) {   }
    // 删除第一次出现的关键字 key
    public void remove ( int toRemove ) {   }
    // 获取顺序表长度
    public int size () { return 0 ; }
    // 清空顺序表
    public void clear () {   }
}

2.3 顺序表的问题及思考

        1.顺序表中间/头部的插入删除,时间复杂度O(N)

        2.增容需要申请新空间,拷贝数据,释放空间,会有不小消耗

        3.增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续 插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

3.链表

3.1链表的概念及结构

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

八大结构

3.1.1单链表、双向链表 

3.1.2不带头单链表、带头链表

3.1.3单链表、循环单链表

 3.2 链表的实现

//3.2.1 无表头非循环链表实现

public class SingleLinkedList {
     //头插法
     public void addFirst(int data);
     //尾插法
     public void addLast(int data);
     //任意位置插入,第一个数据节点为0号下标
     public boolean addIndex(int index,int data);
     //查找是否包含关键字key是否在单链表当中
     public boolean contains(int key);
     //删除第一次出现关键字为key的节点
     public void remove(int key);
     //删除所有值为key的节点
     public void removeAllKey(int key);
     //得到单链表的长度
     public int size();
     public void display();
     public void clear();
 }

 //3.2.1无头双向链表实现

public class DoubleLinkedList {
     //头插法
     public void addFirst(int data);
     //尾插法
     public void addLast(int data);
     //任意位置插入,第一个数据节点为0号下标
     public boolean addIndex(int index,int data);
     //查找是否包含关键字key是否在单链表当中
     public boolean contains(int key);
     //删除第一次出现关键字为key的节点
     public void remove(int key);
     //删除所有值为key的节点
     public void removeAllKey(int key);
     //得到单链表的长度
     public int size();
     public void display();
     public void clear();
 }

 3.3oj

1.删除链表中等于给定值“val”的所有节点

剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode) (leetcode-cn.com)

/**
 * 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 head;
        ListNode cur = head;
        ListNode pre = null;
        if(cur.val == val) return head.next;
        while(cur.val !=val){
            pre = cur;
            cur = cur.next;
        }
        pre.next = pre.next.next;
        return head;
    }
}

2.反转一个单链表

剑指 Offer 24. 反转链表 - 力扣(LeetCode) (leetcode-cn.com)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
  //如果链表为空,或者只有一个元素,则直接返回该链表
        if (head==null||head.next==null){
            return head;
        }
        //定义一个辅助节点遍历原来的链表
        ListNode cur=head;
        //指向下一个节点
        ListNode next=null;
        ListNode  newNode= new ListNode(0);
        while (cur!=null){
            //存储cur 的下一个节点
            next=cur.next;
            cur.next=newNode.next;
            newNode.next=cur;
            cur=next;
        }
        return newNode.next;
    }
}

剑指 Offer II 024. 反转链表 - 力扣(LeetCode) (leetcode-cn.com)

/**
 * 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 reverseList(ListNode head) {
        if(head==null) return head;
        ListNode pre=null;
        ListNode cur=head;
        while(cur !=null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre =cur;
            cur = temp;
//就像是交换的函数 头尾不断向中间靠近的同时进行交换 直到相等
        }
        return pre;

    }
}

3.给定一个带有头节点head的飞控单链表,但会利埃纳表的中间节点

Loading Question... - 力扣(LeetCode) (leetcode-cn.com)

/**
 * 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 middleNode(ListNode head){
        ListNode fast,slow;
        fast=head;
        slow=head;                      //都从头开始

        while(fast !=null && fast.next !=null){
            slow=slow.next;             //慢指针,一次走一步
            fast = fast.next.next;      //快指针,一次走两步
        }
        return slow;                   
 //快是慢的速度两倍,但同时路程也是两倍,所以返回慢就等于返回了一半
    }
}

4.输入一个链表,输出该链表中倒数第k个数

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode pre=null,p=null;
        //两个指针都指向结点
        p=head;
        pre=head;
        //记录k值
        int a=k;
        //记录节点的个数
        int count=0;
        //p指针先跑,并且记录节点树,当指针跑了k-1个节点后,pre指针开始跑
        //当p指针跑到最后时,pre所指指针就是倒数第k个节点
        while(p!=null){
            p=p.next;
            count++;
            if(k<1){
                pre=pre.next;
            }
            k--;
        }
        //如果节点个数小于所求的倒数第k个节点,则返回空
        if(count<a)return null;
        return pre;
    }
}

5.将两个有序链表合并为一个新的有序链表并返回,新链表是通拼接给定的两个链表的所有节点组成

21. 合并两个有序链表 - 力扣(LeetCode) (leetcode-cn.com)

/**
 * 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 dummyHead = new ListNode(0);
        ListNode cur = dummyHead;
        while (l1 != null && l2 != null) 
        {
            if (l1.val < l2.val) 
            {
                cur.next = l1;
                cur = cur.next;
                l1 = l1.next;
            } else 
            {
                cur.next = l2;
                cur = cur.next;
                l2 = l2.next;
            }
        }
        // 任一为空,直接连接另一条链表
        if (l1 == null) 
        {

            cur.next = l2;
        } else {
            cur.next = l1;
        }
        return dummyHead.next;
    }
}

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

链表分割_牛客题霸_牛客网 (nowcoder.com)

import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Partition {
ListNode testHead=null;
ListNode testTail=null;
//2.创建新链表需要的元素:
//@1两个链表的头节点

ListNode head1=null;
ListNode head2=null;
//3.创建操作元素
ListNode tail1=null;
ListNode tail2=null;
public ListNode partition(ListNode pHead, int x) {
// write code here
//1.接收链表头的第一个节点
ListNode current=pHead;
/* while(pHead!=null){
System.out.print(pHead.val+" ");
pHead=pHead.next;
//testHead=testHead.next;
}*/
while(current!=null){
//如果小于x,则装入链表1,
if(current.val<x){
insert1(current.val);
//如果大于或等于x,则装入链表2
}else{
insert2(current.val);
}
//下一个链表节点
current=current.next;
}
//对两链表进行拼接
if(head1==null&&head2==null){
return null;
}else if(head1==null&&head2!=null){
return head2;
}else if(head1!=null&&head2==null){
return head1;
}else{
//两链表拼接
tail1.next=head2;
return head1;
}

}
public void insert1(int result){
//首先创建一个新节点
ListNode node=new ListNode(result);
if(head1==null){
head1=node;
tail1=node;
System.out.println("链表1 "+head1.val+" ");
}else{
tail1.next=node;
tail1=node;
}
}
public void insert2(int result){
//首先创建一个新节点
ListNode node=new ListNode(result);
if(head2==null){
head2=node;
tail2=node;
System.out.println("链表2 "+head2.val+" ");
}else{
tail2.next=node;
tail2=node;
}
}
}

7.在一个排序的链表中,存在重复的节点,请删除该链表中重复节点,重复的节点不保留,返回链表头指针

​​​​​​删除链表中重复的结点_牛客题霸_牛客网 (nowcoder.com)

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        ListNode dummy = new ListNode(-1);
        ListNode tail = dummy;
        while (pHead != null) {
            // 进入循环时,确保了 pHead 不会与上一节点相同
            if (pHead.next == null || pHead.next.val != pHead.val) {
                tail.next = pHead;
                tail = pHead;
            }
            // 如果 pHead 与下一节点相同,跳过相同节点(到达「连续相同一段」的最后一位)
            while (pHead.next != null && pHead.val == pHead.next.val) pHead = pHead.next;
            pHead = pHead.next;
        }
        tail.next = null;
        return dummy.next;
    }
}

8.链表的回文结构

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

import java.util.*;

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class PalindromeList {

    public static Stack<Integer> stack = new Stack<Integer>();
    public static boolean chkPalindrome(ListNode A) {
        // write code here
        ListNode quick = A;
        ListNode slow = A;
        //快慢指针 得到mid
        while (quick != null && quick.next != null) {
            stack.push(slow.val);
            quick = quick.next.next;
            slow = slow.next;
        }
        if (quick == null) {   //偶数,正好得到一半后的第一个

        } else {    //quick.next == null   基数,得到中间的那个
            stack.pop();
            slow = slow.next;
        }

        while (slow != null) {
            if ((slow.val) != stack.pop()) {
                return false;
            }
           slow = slow.next;
        }
        return true;
    }
    public static void main(String[] args) {
        ListNode A = new ListNode(1);
        ListNode B = new ListNode(2);
        ListNode C = new ListNode(2);
        ListNode D = new ListNode(1);
        A.next = B;
        B.next = C;
        C.next = D;
        System.out.println(chkPalindrome(A));
    }
}

9.输入两个链表,找出它们的第一个公共节点

Loading Question... - 力扣(LeetCode) (leetcode-cn.com)

可跑就是时间超了。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        if (null == headA || null == headB) {
            return null;
        }

        // 辅助节点
        ListNode p1 = headA, p2 = headB, p3 = headA, p4 = headB;

        // 遍历两个链表,直到某一个结束
        while (null != p1.next && null != p2.next) {
            p1 = p1.next;
            p2 = p2.next;
        }

        // 继续遍历未遍历完的链表,保证两个链表尾部对齐,从相同长度的节点出发
        while (null != p1.next) {
            p3 = p3.next;
            p1 = p1.next;
        }
        while (null != p2.next) {
            p4 = p4.next;
            p2 = p2.next;
        }

        // 尾部对齐从相同长度的节点出发遍历
        while (null != p3 && null != p4) {
            if (p3 == p4) {
                // 如果两个节点相同,直接返回
                return p3;
            }

            p3 = p3.next;
            p4 = p4.next;
        }

        return null;
    }
}
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        ListNode n1 = headA;
        ListNode n2 = headB;
        
        while(n1 != n2){
            n1 = n1 == null ? headB : n1.next;
            n2 = n2 == null ? headA : n2.next;
        }
        return n1;
    }
}

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

/**
 * 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||head.next==null) return null;
        ListNode l=head; ListNode r=head;
        while(r!=null){
            l=l.next; r=r.next;
            if(r!=null) r=r.next;
            if(l==r){
                l=head;
                while(l!=r){
                    l=l.next;
                    r=r.next;
                }
                return l;
            }
        }
        return null;
        
    }
}

  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值