算法题② —— 链表专栏

1. 链表数据结构

struct ListNode {
	int val;
	ListNode *next;
	ListNode() : val(0), next(nullptr) {}
	ListNode(int x) : val(x), next(nullptr) {}
	ListNode(int x, ListNode *next) : val(x), next(next) {}
  };

2. 链表的删除

2.1 移除链表元素

  • 力扣:https://leetcode.cn/problems/remove-linked-list-elements/description/
ListNode* removeElements(ListNode* head, int val) {
	if(head == NULL) return NULL;
	ListNode *dummy= new ListNode(-1);
    dummy->next = head;
    ListNode *pre = dummy;
    ListNode *cur = head;
    while(cur){
        if(cur->val == val){
            ListNode *tmp = cur->next;
            pre->next = tmp;
            delete(cur);
            cur = tmp;
        }
        else{
            pre = cur;
            cur = cur->next;
        }
    }
    return dummy->next;
}

2.2 删除链表的倒数第N个节点

  • 力扣:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/
ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode* dummyHead = new ListNode(-1);
    dummyHead->next = head;
    ListNode* slow = dummyHead;
    ListNode* fast = dummyHead;
    while(n-- && fast) {
        fast = fast->next;
    }
    fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
    while (fast) {
        fast = fast->next;
        slow = slow->next;
    }
    slow->next = slow->next->next; 
    ListNode *tmp = slow->next;
    slow->next = tmp->next;
    delete tmp;
    return dummyHead->next; 
}

3. 链表的移动

3.1 反转链表

  • 力扣:https://leetcode.cn/problems/reverse-linked-list/description/
ListNode* reverseList(ListNode* head) {
    if(head == NULL) return NULL;
    if(head->next == NULL) return head;
    ListNode *pre = NULL;
    ListNode *cur = head;
    while(cur){
        ListNode *tmp = cur->next;
        cur->next = pre;
        pre = cur;
        cur = tmp;
    }
    return pre;
}

3.2 两两交换链表节点

  • 力扣:https://leetcode.cn/problems/swap-nodes-in-pairs/description/
ListNode* swapPairs(ListNode* head) {
    if(head == NULL) return NULL;
    if(head->next == NULL) return head;
    ListNode *dummy = new ListNode(-1);
    dummy->next = head;
    ListNode *pre = dummy;
    ListNode *cur = head;
    while(cur && cur->next){
        ListNode *tmp = cur->next;
        cur->next = tmp->next;
        tmp->next = cur;
        pre->next = tmp;
        pre = cur;
        cur = cur->next;
    }
    return dummy->next;
}

3.3 反转链表的一部分

  • 力扣:https://leetcode.cn/problems/reverse-linked-list-ii/description/
ListNode *reverseBetween(ListNode *head, int left, int right) {
    ListNode *dummy = new ListNode(-1);
    dummy->next = head;
    ListNode *pre = dummy;
    // 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
    for (int i = 0; i < left - 1; i++) {
        pre = pre->next;
    }
    // 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
    ListNode *rightNode = pre;
    for (int i = 0; i < right - left + 1; i++) {
        rightNode = rightNode->next;
    }
    // 第 3 步:切断出一个子链表(截取链表)
    ListNode *leftNode = pre->next;
    ListNode *curr = rightNode->next;
    pre->next = nullptr;
    rightNode->next = nullptr;
    // 第 4 步:同上一题,反转链表的子区间
    reverseLinkedList(leftNode);
    // 第 5 步:接回到原来的链表中
    pre->next = rightNode;
    leftNode->next = curr;
    return dummy->next;
}

3.4 K个一组反转链表

  • 力扣:https://leetcode.cn/problems/reverse-nodes-in-k-group/description/
ListNode* reverseKGroup(ListNode* head, int k) {
    ListNode *dummy = new ListNode(-1);
    dummy->next = head;         
    ListNode *left = head;       //每组的第一个
    ListNode *right = dummy;     //每组的第k个,初始为left的前一个节点
    ListNode *beforePre = dummy; //反转前本组的前驱
    while(left){
        for(int i = 0; i < k; ++i){
            if(right->next) right = right->next;
            else return dummy->next;  //不足k个结点,反转结束
        }
        ListNode *beforeNext = right->next; //即为下一组的left
        ListNode *pre = nullptr;
        ListNode *cur = left;
        while(pre != right){
            ListNode *tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        beforePre->next = right;
        left->next = beforeNext;
        right = left;
        beforePre = left;
        left = beforeNext;
    }
    return dummy->next;
}

3.5 旋转链表

  • 力扣:https://leetcode.cn/problems/rotate-list/description/

3.6 分隔链表

  • 力扣:https://leetcode.cn/problems/partition-list/description/

4. 链表的相交

4.1 链表相交

  • 力扣:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    ListNode* curA = headA;
    ListNode* curB = headB;
    int lenA = 0, lenB = 0;
    while (curA) { 
        lenA++;
        curA = curA->next;
    }
    while (curB) { 
        lenB++;
        curB = curB->next;
    }
    curA = headA;
    curB = headB;
    int gap = 0;
    if (lenB > lenA) {
        gap = lenB -lenA;
        while(gap--) curB = curB->next;
    }
    else{ 
        gap = lenA - lenB;
        while (gap--) curA = curA->next;
    }
    while (curA) {
        if (curA == curB) return curA;
        curA = curA->next;
        curB = curB->next;
    }
	return NULL;
}

4.2 合并链表

  • 力扣:https://leetcode.cn/problems/merge-two-sorted-lists/description/

4.3 环形链表

  • 力扣:https://leetcode.cn/problems/linked-list-cycle-ii/description/
ListNode *detectCycle(ListNode *head) {
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            ListNode* index1 = fast;
            ListNode* index2 = head;
            while (index1 != index2) {
                index1 = index1->next;
                index2 = index2->next;
            }
            return index2; // 返回环的入口
        }
    }
    return NULL;
}
  • 判断环入口的方法:
    • 相遇时,slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z)
    • 因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 (x + y) * 2 = x + y + n (y + z),得到 x = n (y + z) - y
    • 整理公式之后为如下公式:x = (n - 1) (y + z) + z
    • n = 1 时,得到 x = z,这就意味着:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

在这里插入图片描述

5. 其他

5.1 LRU缓存(链表常考题)

  • 力扣:https://leetcode.cn/problems/lru-cache/description/

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈希表是一种高效的数据结构,可以用来存储和查找键值对。其中,哈希函数将键映射到一个特定的桶中,每个桶中存储一组键值对。在哈希表中,如果两个键被映射到同一个桶中,就会发生碰撞。为了解决这个问,可以使用链表法。 链表法是一种解决哈希表碰撞问的方法。具体来说,对于哈希表中的每个桶,可以使用一个链表来存储所有映射到该桶的键值对。如果发生碰撞,只需要将新的键值对添加到链表的末尾即可。 下面是一个使用链表法实现哈希表的示例代码: ```python class Node: def __init__(self, key, value): self.key = key self.value = value self.next = None class HashTable: def __init__(self, capacity): self.capacity = capacity self.buckets = [None] * capacity def hash_function(self, key): return hash(key) % self.capacity def put(self, key, value): index = self.hash_function(key) node = self.buckets[index] while node: if node.key == key: node.value = value return node = node.next new_node = Node(key, value) new_node.next = self.buckets[index] self.buckets[index] = new_node def get(self, key): index = self.hash_function(key) node = self.buckets[index] while node: if node.key == key: return node.value node = node.next return None def remove(self, key): index = self.hash_function(key) node = self.buckets[index] prev = None while node: if node.key == key: if prev: prev.next = node.next else: self.buckets[index] = node.next return prev = node node = node.next ``` 在这个示例中,我们定义了一个Node类来表示哈希表中的每个节点,每个节点包含一个键、一个值和一个指向下一个节点的指针。我们还定义了一个HashTable类来实现哈希表,其中包含一个桶数组和一些基本的操作方法,如put、get和remove。 在put方法中,我们首先使用哈希函数计算出键的索引,然后遍历桶中的链表,查找该键是否已经存在于哈希表中。如果找到了该键,我们只需要更新其对应的值即可。否则,我们创建一个新的节点,并将其添加到链表的开头。 在get方法中,我们同样使用哈希函数计算出键的索引,然后遍历桶中的链表,查找该键的值。如果找到了该键,我们返回其对应的值。否则,返回None。 在remove方法中,我们首先使用哈希函数计算出键的索引,然后遍历桶中的链表,查找该键。如果找到了该键,我们将其从链表中删除即可。 总的来说,链表法是一种简单且常用的哈希表解决碰撞问的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值