算法挑战 Day3 - 链表

80 阅读2分钟

Day3 链表

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

 

示例 1:

输入: head = [1,2,6,3,4,5,6], val = 6
输出: [1,2,3,4,5]

示例 2:

输入: head = [], val = 1
输出: []

示例 3:

输入: head = [7,7,7,7], val = 7
输出: []

 

提示:

  • 列表中的节点数目在范围 [0, 104] 内
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

解题思路

使用虚拟头结点解决,这样可以不需要考虑头结点就为需要删除的节点的问题

实现代码

func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? {
        // 创建虚拟头节点
        let dummyNode = ListNode()
        dummyNode.next = head
        
        // 让当前节点等于 虚拟头节点
        var cur:ListNode? = dummyNode
        
        // 从当前节点遍历 只要 cur?.next != nil
        while cur?.next != nil {
            // 如果 cur?.next?.val == val 则删除 cur?.next? 节点 否则 cur  = cur?.next
            if cur?.next?.val == val {
                cur?.next = cur?.next?.next
            } else {
                cur = cur?.next
            }
        }
        
        return dummyNode.next
    }

707. 设计链表

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

 

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3

 

提示:

  • 0 <= index, val <= 1000
  • 请不要使用内置的 LinkedList 库。
  • 调用 getaddAtHeadaddAtTailaddAtIndex 和 deleteAtIndex 的次数不超过 2000

解题思路

实现链表的方法,可以在链表类里面 初始化一个头节点 以及一个 尾节点,每一个方法的实现都需要考虑头节点 以及 尾节点 是否需要更新。

实现代码


// 创建一个 链表节点的类 包含节点的属性 以及 节点的初始化方法
class MyListNode {
    public var val: Int
    public var next: MyListNode?
    public init() { self.val = 0; self.next = nil; }
    public init(_ val: Int) { self.val = val; self.next = nil; }
    public init(_ val: Int, _ next: MyListNode?) { self.val = val; self.next = next; }
}


public class MyLinkedList {
    
    // 定义 头节点
    var head:MyListNode?
    
    // 定义 尾节点
    var tail:MyListNode?
    
    // 初始化 方法 ,头节点 尾节点 都是 nil
    init() {
        head = nil
        tail = nil
    }
    
    // 要或者index 这个位置的值,则只需要知道index 这个位置的节点 ,并取出节点里的值 即可
    public func get(_ index: Int) -> Int {
        var curNode = head
        for _ in 0..<index {
            curNode = curNode?.next
        }
        return curNode?.val ?? -1
    }
    
    // 添加到头部 ,直接创建节点 并传入这个节点的后继节点为 head
    // 更新 head 为新创建的节点
    // 根据 tail 节点的状态 判断是否需要进行更新 【tail 为nil 需要进行更新 因为之前tail不存在,新创建的这个节点 既是头节点 又是尾节点】
    public func addAtHead(_ val: Int) {
        let listNode = MyListNode(val,head)
        head = listNode
        if tail == nil {
            tail = head
        }
    }
    
    // 添加到尾部 ,直接创建节点
    // 如果尾节点为 nil 说明之前链表没有节点,此时需要将头节点 以及 尾节点 都指向这个新创建的节点
    // 如果尾节点不为 nil ,首先需要将 尾节点的 next 指针指向新创建的节点 ,同时更新 tail 尾节点 为这个新创建的节点
    public func addAtTail(_ val: Int) {
        let listNode = MyListNode(val)
        if tail == nil {
            tail = listNode
            head = listNode
        } else {
            tail?.next = listNode
            tail = listNode
        }
    }
    
    // 添加值到指的位置
    // 如果index == 0 表示就是添加到头节点的位置
    // 否则的话 就是 找 链表里面 index-1 的这个节点
    // 如果这个节点存在 则新建一个节点 ,在这个节点next 指向新增加的节点 同时判断是否需要更新尾节点
    public func addAtIndex(_ index: Int, _ val: Int) {
        if index == 0 {
            addAtHead(val)
        } else {
            var curNode = head
            for _ in 0..<index-1 {
                curNode = curNode?.next
            }
            if curNode != nil {
               let listNode = MyListNode(val,curNode?.next)
                curNode?.next = listNode
                if listNode.next == nil {
                    tail = listNode
                }
            }
        }
    }
    
    // 删除指定位置的值
    // 如果index == 0 更新 head 头节点的指针
    // 否则的话 就是 找 链表里面 index-1 的这个节点
    // 如果这个节点存在 则删除这个节点之后的节点 同时判断是否需要更新尾节点
    public func deleteAtIndex(_ index: Int) {
        if index == 0 {
            head = head?.next
        } else {
            var curNode = head
            for _ in 0..<index-1 {
                curNode = curNode?.next
            }
            
            if curNode != nil {
                curNode?.next = curNode?.next?.next
                if curNode?.next == nil {
                    tail = curNode
                }
            }
        }
    }
    
}

206. 反转链表

labuladong 题解思路

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

 

示例 1:

输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]

示例 2:

输入: head = [1,2]
输出: [2,1]

示例 3:

输入: head = []
输出: []

 

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

 

进阶: 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解题思路

递归实现反转链表常常用来考察递归思想,我这里就用纯递归来翻转链表。

对于递归算法,最重要的就是明确递归函数的定义。具体来说,我们的 reverse 函数定义是这样的:

输入一个节点 head,将「以 head 为起点」的链表反转,并返回反转之后的头结点

明白了函数的定义,再来看这个问题。比如说我们想反转这个链表:

那么输入 reverse(head) 后,会在这里进行递归:

Copy

ListNode last = reverse(head.next);

不要跳进递归(你的脑袋能压几个栈呀?),而是要根据刚才的函数定义,来弄清楚这段代码会产生什么结果:

这个 reverse(head.next) 执行完成后,整个链表就成了这样:

并且根据函数定义,reverse 函数会返回反转之后的头结点,我们用变量 last 接收了。

现在再来看下面的代码:

Copy

head.next.next = head;

接下来:

Copy

head.next = null;
return last;

神不神奇,这样整个链表就反转过来了!

实现代码

func reverseList(_ head: ListNode?) -> ListNode? {
        // 如果head 为 nil 或者 head?.next 为nil 说明没有必要进行反转
        if head == nil || head?.next == nil {
            return head
        }
        
        // 首先将头节点的下一个节点 调用递归进行反转
        let res = reverseList(head?.next)
        // head.next 指向 反转后的那个链表 ,它的next 指向head 本身
        head?.next?.next = head
        // 将head 的next 指针指向 nil
        head?.next = nil
        return res
    }