链表相关的习题

目录

一、链表的创建及基本增删改查操作

1.IList接口

2.MySingleLinkedList类

3.Test测试类

二、删除链表中给定值val的所有节点

三、反转一个链表

四、返回中间节点

五、合并两个有序链表

六、链表的回文结构

七、输出链表倒数第k个节点

八、输入值x,小于在前,大于在后

九、输入两个链表,找出它们的第一个公共结点

十、给定一个链表,判断链表中是否有环

十一、给定一个链表,返回链表开始入环的第一个节点。 


一、链表的创建及基本增删改查操作

1.IList接口

public interface IList {
    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public void 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();
}

2.MySingleLinkedList类

public class MySingleLinkedList implements IList {
    static class ListNode{
        public int val;
        public ListNode next;

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

    //定义全局变量首节点
    public ListNode head;

    public void createList(){
        //创建5个节点
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);

        //连接5个节点,直接存入node对象,其实存入的就是地址
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = null;

        //局部变量node1会在函数结束以后销毁,所以将其赋给全局变量head
        this.head = node1;
    }

    @Override
    public void addFirst(int data) {
        ListNode firstNode = new ListNode(data);
        if (this.head == null){
            this.head = firstNode;
        }else {
            firstNode.next = this.head;
            this.head = firstNode;
        }
    }

    @Override
    public void addLast(int data) {
        ListNode lastNode = new ListNode(data);
        ListNode currNode = this.head;
        if (this.head == null) {
            this.head = lastNode;
        } else {
            //此处可理解为不进入currNode,所以打印不出来它的val
            //这就和display中currNode!=null形成了对比,
            //因为display中需要进入到currNode去找到val值
            while (currNode.next != null) {
                currNode = currNode.next;
            }
            currNode.next = lastNode;
        }
    }

    @Override
    public void addIndex(int index, int data) {

        if (index<0 || index > size()){
            System.out.println("插入位置不合理");
            return ;
        }
        if (index == 0){
            addFirst(data);
            return ;
        }
        if (index == size()){
            addLast(data);
            return;
        }

        ListNode dataNode = new ListNode(data);
        ListNode currNode = this.head;
        int count = 0;
        while (count < index-1){
            count++;
            currNode = currNode.next;
        }
        dataNode.next = currNode.next;
        currNode.next = dataNode;
    }

    @Override
    public boolean contains(int key) {
       ListNode cur = head;
        while (cur!=null){
            if (cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    @Override
    public void remove(int key) {
        if (this.head == null){
            System.out.println("链表无节点");
            return ;
        }
        if (this.head.val == key){
            this.head = this.head.next;
            return;
        }

        ListNode currNode = this.head;
        while (currNode.next != null) {
            if (currNode.next.val == key) {
                currNode.next = currNode.next.next;
                break;
            } else {
                currNode = currNode.next;
            }
        }
        if (currNode.next == null){
            System.out.println("没有你要删除的数");
        }
    }

    @Override
    public void removeAllKey(int key) {
        if(head == null){
            return ;
        }

        ListNode prev = head;
        ListNode cur = head.next;
        while (cur!=null){
            if (cur.val == key){
                prev.next = cur.next;
                cur = cur.next;
            }else {
                prev = cur;
                cur = cur.next;
            }
        }
        if(head.val == key){
            head = head.next;
        }
    }

    @Override
    public int size() {
        int count = 0;
        ListNode currNode = this.head;
        while (currNode != null){
            count++;
            currNode = currNode.next;
        }
        return count;
    }

    @Override
    public void display() {
        ListNode currNode = this.head;
        while (currNode != null){
            System.out.print(currNode.val + " ");
            currNode = currNode.next;
        }
        System.out.println();

    }

    @Override
    public void clear() {
        ListNode cur = head;
        while (cur!=null){
            ListNode curNext = cur.next;
            cur.next = null;
            cur = cur.next;
        }
        head = null;
    }
    }
}

3.Test测试类


public class Test { 
    public static void main(String[] args) {
        MySingleLinkedList mySingleLinkedList1 = new MySingleLinkedList();
        mySingleLinkedList1.createList();
        mySingleLinkedList1.addFirst(6);
        mySingleLinkedList1.addFirst(7);
//        System.out.println(mySingleLinkedList1.contains(7));
        mySingleLinkedList1.display();

        MySingleLinkedList mySingleLinkedList2 = new MySingleLinkedList();
        mySingleLinkedList2.createList();
        mySingleLinkedList2.addFirst(7);
        mySingleLinkedList2.addFirst(8);
        mySingleLinkedList2.display();

        mergeTwoLists(mySingleLinkedList1.head,mySingleLinkedList2.head);

//        System.out.println("size:" + mySingleLinkedList.size());
//        System.out.println("==================");
//
//        mySingleLinkedList.addLast(999);
//        mySingleLinkedList.display();
//
//        System.out.println("==================");
//        mySingleLinkedList.addIndex(1,123);
//        mySingleLinkedList.addIndex(9,5876);
//        mySingleLinkedList.display();
//
//        System.out.println("===============");
        mySingleLinkedList.remove(123);
        mySingleLinkedList.remove(1234);
//        mySingleLinkedList.remove(5876);
//        mySingleLinkedList.display();
//        System.out.println("test====================");
//        mySingleLinkedList.display();
//        mySingleLinkedList.reverseList();
//        mySingleLinkedList.display();
//        System.out.println("mid==============");
//        mySingleLinkedList.middleNode1();
//        mySingleLinkedList.display();
//        mySingleLinkedList.middleNode2();
//        mySingleLinkedList.display();
//        System.out.println("kkk");
//        mySingleLinkedList.FindKthToTail(2);
//        mySingleLinkedList.display();

    }
    public static MySingleLinkedList.ListNode mergeTwoLists(MySingleLinkedList.ListNode head1,
                                               MySingleLinkedList.ListNode head2) {
        MySingleLinkedList.ListNode newH = new MySingleLinkedList.ListNode(-1);
        MySingleLinkedList.ListNode tmpH = newH;
        while (head1 != null && head2 != null) {
            if(head1.val < head2.val) {
                tmpH.next = head1;
                head1 = head1.next;
                tmpH = tmpH.next;
            }else {
                tmpH.next = head2;
                head2 = head2.next;
                tmpH = tmpH.next;
            }
        }
        if (head1 != null) {
            tmpH.next = head1;
        }
        if (head2 != null) {
            tmpH.next = head2;
        }
        return newH.next;
    }
}

二、删除链表中给定值val的所有节点

正常遍历+比值即可

public void removeAllKey(int key) {
        if(head == null){
            return ;
        }

        ListNode prev = head;
        ListNode cur = head.next;
        while (cur!=null){
            if (cur.val == key){
                prev.next = cur.next;
                cur = cur.next;
            }else {
                prev = cur;
                cur = cur.next;
            }
        }
        if(head.val == key){
            head = head.next;
        }
    }

三、反转一个链表

思路:每次都将head后的节点,头插到head前面。

技巧:每次都要先将cur.next的值赋给一个变量保存起来,再将cur拿去偷插。第二个节点cur = head.next,第三个节点currNext = cur.next。

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

四、返回中间节点

思路1:中间节点位置为总长len/2。先遍历一遍获取len,再遍历一边获得mid。

public ListNode middleNode1(){
        int ListCount = 0;  //列表长度
        ListNode curr = head;
        while (curr != null){
            ListCount ++;
            curr = curr.next;
        }
        int count2 = 0;
        ListNode curr2 = head;
        while (count2 < ListCount/2){
            count2 ++;
            curr2 = curr2.next;
        }
        this.head = curr2;
        return this.head;
    }

思路2:双指针模型,让fast和slow都从head出发,fast一次走两步,slow一次走一步。走完以后slow的节点就是中间节点。(双指针的思路,很多算法题都可用)

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

五、合并两个有序链表

思路:创建一个起始虚拟节点newT,将其赋值给tempT。head1、head2分别指向两条链表。对比head1和head2指向的值,谁小把谁加到tempT后面。tempT需要更新,永远指向最后一步操作所指向的节点。

public static MySingleLinkedList.ListNode mergeTwoLists(
                                    MySingleLinkedList.ListNode head1,
                                    MySingleLinkedList.ListNode head2) {
        MySingleLinkedList.ListNode newH = new MySingleLinkedList.ListNode(-1);
        MySingleLinkedList.ListNode tmpH = newH;
        while (head1 != null && head2 != null) {
            if(head1.val < head2.val) {
                tmpH.next = head1;
                head1 = head1.next;
                tmpH = tmpH.next;
            }else {
                tmpH.next = head2;
                head2 = head2.next;
                tmpH = tmpH.next;
            }
        }
        if (head1 != null) {
            tmpH.next = head1;
        }
        if (head2 != null) {
            tmpH.next = head2;
        }
        return newH.next;
    }

六、链表的回文结构

思路:将中间节点到末尾节点的Node反转。1.找到中间位置;2.反转后面的节点;3.从前到后,从后往前,对比。

public boolean chkPalindrome() {
        if (head==null){
            return true;
        }
        //1.找到中间位置
        ListNode fast = head;
        ListNode slow = head;
        while(fast!=null && fast.next!=null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //2.翻转
        ListNode cur = slow.next;
        while(cur!=null){
            ListNode curNext = cur.next;
            cur.next = slow;
            cur = cur.next;
            slow = cur;
            cur = curNext;
        }
        //3.从前到后,从后到前判断
        while (head!=slow){
            if (head.val != slow.val){
                return false;
            }
            if (head.next == slow){
                return true;
            }
            head = head.next;
            slow = slow.next;
        }
        return true;
    }

七、输出链表倒数第k个节点

思路:双指针思路,先让fast走k-1步,然后fast和slow一起走,直到fast走完,此时slow的位置即为倒数第k个节点。

public ListNode FindKthToTail(int k) {
        //判断k的合法性
        if (k<=0 || head==null){
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        for (int i = 0; i < k-1; i++) {
            fast = fast.next;
            //处理k太大问题
            if (fast == null){
                return null;
            }
        }
        while (fast.next!=null){
            fast = fast.next;
            slow = slow.next;
        }
        head = slow;
        return slow;
    }

八、输入值x,小于在前,大于在后

思路:遍历原链表,将小于的用尾插法存入一个bs、be链表,大于的存入as、ae链表,最后连接起来

public ListNode div(int k){
    if (head==null){
        return null;
    }
    ListNode bs = null;
    ListNode be = null;

    ListNode as = null;
    ListNode ae = null;

    ListNode curr = head;
    //遍历每个节点
    while (curr!=null){
        //比k小
        if (curr.val < k){
            //如果是第一次插入
            if (bs == null){
                //此时插入第一个节点
                bs = curr;
                be = curr;
            }else {
                //be永远指向最后一个节点
                be.next = curr;
                be = be.next;
            }
        }else {
            if (as == null){
                //此时插入第一个节点
                as = curr;
                ae = curr;
            }else {
                //ae永远指向最后一个节点
                ae.next = curr;
                ae = ae.next;
            }
        }
        curr = curr.next;
    }
    //开始将小于串、k、大于串,串起来
    if (bs == null){
        return as;
    }
    be.next = as;
    if (as != null){
        //说明第二个区间有数据,需要将第二个区间最后一个元素next设为null
        ae.next = null;
    }
    head = bs;
    return head;
}

九、输入两个链表,找出它们的第一个公共结点 

思路:计算两个链表的长度差k,让长的链表先走k步,然后两个链表一起走,相同点就是公共节点。

//找出第一个相交节点
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    int count1 = 0;
    int count2 = 0;

    ListNode curr1 = headA;
    ListNode curr2 = headB;

    //计算两个链表的长度
    while (curr1!=null){
        curr1 = curr1.next;
        count1 ++;
    }
    while (curr2!=null){
        curr2 = curr2.next;
        count2 ++;
    }
    //计算二者差值
    int gap = Math.abs(count1-count2);
    //如果head1长
    curr1 = headA;
    curr2 = headB;
    if (count1>count2){
        //先让curr1走gap步
        for (int i = 0; i < gap; i++) {
            curr1 = curr1.next;
        }
        //再一起走
        while (curr1!=curr2){
            curr1 = curr1.next;
            curr2 = curr2.next;
        }
        return curr1;
    }else {
        //先让curr2走gap步
        for (int i = 0; i < gap; i++) {
            curr2 = curr2.next;
        }
        //再一起走
        while (curr1!=curr2){
            curr2 = curr2.next;
            curr1 = curr1.next;
        }
        return curr2;
    }
}

十、给定一个链表,判断链表中是否有环

思路:让fast一次走2步,slow一次走1步,如果有第一次相同的地方就代表有环。

//判断是否有环
public boolean hasCycle() {
    ListNode fast = head;
    ListNode slow = head;
    //循环判断条件为fast永远走不到头。表示有环就一直在循环里走。
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) return true;
    }
    return false;
}

十一、给定一个链表,返回链表开始入环的第一个节点。 

思路:1.找到相遇的地点;2一个从相遇点出发,一个从起点出发,再次相遇点即为入口。

public ListNode detectCycle() {
    ListNode fast = head;
    ListNode slow = head;
    //循环判断条件为fast永远走不到头。表示有环就一直在循环里走。
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        //如果相遇,就break
        if (fast == slow){
            break;
        }
    }
    if (fast==null || fast.next == null){
        return null;
    }
    //如果有环,程序走到这里,fast指向相遇点,让slow回起点
    //一人一步走
    slow = head;
    while (slow!=fast){
        fast = fast.next;
        slow = slow.next;
    }
    //相遇点即为入口点
    return slow;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值