编程题汇总_链表

题目一:移除链表元素

链接:移除链表元素

删除链表中等于给定值 val 的所有节点。

示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

题目解析

1.删除节点是中间节点,找到删除节点的前一个结点。
在这里插入图片描述
2.删除节点是头节点,直接将 head 后移(最后处理这种情况)。
在这里插入图片描述

程序测试

public class LinkedListTest {

    // 给内部类加 static 效果和其他的 static 类似. 让这个类和 LinkedListTest 类相关, 而和对象不相关.
    // 后续如果要创建 ListNode 的实例的话, 就不需要依赖 LinkedListTest 的实例
    public static class ListNode {
        int val;
        ListNode next;
        ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            //链表为空 直接返回
            return null;
        }

        ListNode prev = head; // prev 始终指向 cur 的前一个位置
        ListNode cur = head.next;
        while (cur != null) {
            if (cur.val == val) {
                //1.要删除的元素是中间节点
                prev.next = cur.next; // cur 对应的节点就被删掉了
                cur = cur.next; // cur 指向下一个元素, 方便进行下次循环
            } else {
                // 不是要删除的节点
                prev = cur;
                cur = cur.next;
            }
        }

        if (head.val == val) {
            //2.要删除的节点是头节点间
            head = head.next;
        }
        return head;
    }
}

题目二:反转链表

链接:反转链表

反转一个单链表。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

题目解析

1.在原链表上直接逆置,创建 3 个引用,分别是 prevcurnext
2.利用三个引用逆置链表。
在这里插入图片描述
3.当 next=null 时,cur就是原链表的最后一个结点,也是新链表的 header 。
在这里插入图片描述

程序测试

   public ListNode reverseList(ListNode head) {
        if (head == null) {
            //空链表不需要逆置
            return head;
        }
        if (head.next == null) {
            //链表只有一个结点不需要逆置
            return head;
        }

        ListNode newHeader = null;
        ListNode cur = head;
        ListNode prev = null;
        while (cur != null) {
            ListNode next = cur.next;
            if (next == null) {
                newHeader = cur; // cur 就是原链表的尾结点 新链表的 head
            }
            cur.next = prev; //逆置结点

            prev = cur; //后移
            cur = next; //后移
        }
        return newHeader;
    }

题目三:链表的中间节点

链接:链表的中间节点

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

示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL。

示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示:
给定链表的结点数介于 1 和 100 之间。

题目解析

先求链表长度,长度/2,走 长度/2 步。

程序测试

   public ListNode middleNode(ListNode head) {
        int midLength = size(head) / 2;
        ListNode cur = head;
        for(int i = 0; i < midLength; i++){
            cur = cur.next;
        }
        return cur;
    }
    
    public int size(ListNode head) {
        int size = 0;
        ListNode cur = head;
        while (cur != null) {
            size++;
            cur = cur.next;
        }
        return size;
    }

题目四:链表中倒数第k个节点

链接:链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

题目解析

先求链表长度,走 length-k 步。
注意:对 k 值进行合法性判断。

程序测试

   public ListNode FindKthToTail(ListNode head, int k) {
        if (head == null) {
            return null;
        }
        
        int size = size(head);
        if (k <= 0 || k > size) {
            // k 是非法值
            return null;
        }
        
        int steps = size - k;
        ListNode cur = head;
        for (int i = 0; i < steps; i++) {
            cur = cur.next;
        }
        return cur;
    }
    
    public int size(ListNode head) {
        int size = 0;
        ListNode cur = head;
        while (cur != null) {
            size++;
            cur = cur.next;
        }
        return size;
    }

题目五:合并两个有序链表

合并两个有序链表

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

示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

题目解析

1.拿两个引用分别指向两个链表的 head ,比较其 val ,将 val 小的尾插到新链表上,并移动引用。
2.如果新链表为空 newHeader = newNode; newTail = newNode;如果新链表不为空 newHeader.next = newNode; newTail = newNode
3.如果其中一个链表为空,直接将剩余部分移动到新链表上。

注意:步骤2可以优化。

程序测试

版本一
注意:newTail.next = new ListNode(cur1.val),使用创建新结点尾插是防止原链表的cur.next依然指向原链表的下一个结点,并未断开造成的死循环问题。

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }

        ListNode newHeader = null; //结果链
        ListNode newTail = newHeader; //结果链的最后一个结点
        ListNode cur1 = l1;
        ListNode cur2 = l2;
        while (cur1 != null && cur2 != null) {
            if (cur1.val < cur2.val) { // 把 cur1 对应的节点插入到新链表的末尾
                if (newHeader == null) {
                    newHeader = cur1;
                    newTail = cur1;
                }else{
                    newTail.next = new ListNode(cur1.val);
                    newTail = newTail.next;
                }
                cur1 = cur1.next;
            } else { // 把 cur2 对应的节点插入到新链表的末尾
                if (newHeader == null) {
                    newHeader = cur2;
                    newTail = cur2;
                }else{
                    newTail.next = new ListNode(cur2.val);
                    newTail = newTail.next;
                }
                cur2 = cur2.next;
            }
        }

        // 当循环结束时, 意味着当前 cur1 和 cur2 一定有一个到达了链表末尾.
        // 把另外一个没到末尾的剩下的元素都连接在最终链表的尾部
        if(cur1 != null){
            newTail.next = cur2;
        }
        if(cur2 != null){
            newTail.next = cur2;
        }

        return newHeader;
    }

版本二
优化了版本一中对结果链的尾插时两种情况的判断。注意此时返回的是 newHead.next

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            // l1 为空, 最终结果就是 l2
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        ListNode newHead = new ListNode(-1);
        ListNode newTail = newHead;
        ListNode cur1 = l1;
        ListNode cur2 = l2;
        while (cur1 != null && cur2 != null) {
            if (cur1.val < cur2.val) {
                // 把 cur1 对应的节点插入到新链表的末尾
                newTail.next = cur1;
                newTail = newTail.next;
                cur1 = cur1.next;
            } else {
                newTail.next = cur2;
                newTail = newTail.next;
                cur2 = cur2.next;
            }
        }
        // 当循环结束时, 意味着当前 cur1 和 cur2 一定有一个到达了链表末尾.
        // 把另外一个没到末尾的剩下的元素都连接在最终链表的尾部
        if (cur1 == null) {
            newTail.next = cur2;
        } else {
            newTail.next = cur1;
        }
        return newHead.next;
    }

题目六:链表分割

链接:链表分割

编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。
给定一个链表的头指针 ListNode* pHead,请返回重新排列后的链表的头指针。

注意:分割以后保持原来的数据顺序不变。

题目解析

1.定义两个新链表 bigHeadsmalHead(有头结点)。
2.便利原链表,与 k 作比较,比 k 小的尾插到smalHead,比 k 大的尾插到 bigHead
3.把两个链表合并成一个链表 smallTail.next = bigHead.next

程序测试

注意:尾插时一定要创建新的结点。

   public ListNode partition(ListNode pHead, int x) {
        if(pHead == null){
            return null;
        }
        if(pHead.next == null){
            //只有一个结点
            return pHead;
        }

        ListNode bigHead = new ListNode(-1);
        ListNode bigTail = bigHead;
        ListNode smallHead = new ListNode(-1);
        ListNode smallTail = smallHead;
        for(ListNode cur = pHead; cur != null; cur = cur.next){
            if(cur.val<x){
                // 插入到 smallTail 后面, 创建崭新的结点
                // (新结点的 next 一定是 null)
                smallTail.next = new ListNode(cur.val);
                smallTail = smallTail.next;
            }else{
                bigTail.next = new ListNode(cur.val);
                bigTail = bigTail.next;
            }
        }

        smallTail.next = bigHead.next;
        return smallHead.next;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值