【C语言】LeetCode热题100之链表

【C语言】LeetCode热题100之链表


160. 相交链表

简单

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

示例 1:

img

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2:

img

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

img

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:No intersection
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

解法1:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *pA = headA;
    struct ListNode *pB = headB;
    int lenA = 0, lenB = 0;

    while (pA != NULL) {
        lenA++;
        pA = pA->next;
    }
    while (pB != NULL) {
        lenB++;
        pB = pB->next;
    }

    pA = headA, pB = headB;
    if (lenA > lenB) {
        for (int i = 0; i < lenA - lenB; ++i) {
            pA = pA->next;
        }
    } else {
        for (int i = 0; i < lenB - lenA; ++i) {
            pB = pB->next;
        }
    }

    while (pA!= NULL) {
        if (pA == pB) {
            return pA;
        }
        pA = pA->next;
        pB = pB->next;
    } 

    return NULL;
}

解法2:

参考官方题解方法二

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *tmp1 = headA;
    struct ListNode *tmp2 = headB;

    while (tmp1 != tmp2) {
        if (tmp1 == NULL) {
            tmp1 = headB;
        } else {
            tmp1 = tmp1->next;
        }

        if (tmp2 == NULL) {
            tmp2 = headA;
        } else {
            tmp2 = tmp2->next;
        }
    }

    return tmp1;
}

206. 反转链表

简单

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

示例 1:

img

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

示例 2:

img

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

示例 3:

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

提示:

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

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

解法1:迭代

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if (head == NULL) {
        return NULL;
    }

    struct ListNode *pCur = head;
    struct ListNode *pPre = NULL;
    struct ListNode *pNex = NULL;
    while (pCur != NULL) {
        pNex = pCur->next;
        pCur->next = pPre;
        pPre = pCur;
        pCur = pNex;
    }

    return pPre;
}

解法2:递归

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }

    struct ListNode *ret = reverseList(head->next);
    head->next->next = head;
    head->next = NULL;
    return ret;
}

234. 回文链表

简单

给你一个单链表的头节点 head ,请你判断该链表是否为

回文链表

。如果是,返回 true ;否则,返回 false

示例 1:

img

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

示例 2:

img

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

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 #define N 100000

bool isPalindrome(struct ListNode* head) {
    int num[N] = { 0 };
    int cnt = 0;
    while (head != NULL) {
        num[cnt++] = head->val;
        head = head->next; 
    }

    for (int i = 0; i < cnt/2; i++) {
        if (num[i] != num[cnt - 1 - i]) {
            return false;
        }
    }

    return true;
}

141. 环形链表

简单

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode *fast = head;
    struct ListNode *slow = head;
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            return true;
        }
    }
    return false;
}

142. 环形链表 II

中等

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

解法1:快慢指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast = head;
    struct ListNode *slow = head;
    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow) {
            break;
        }
    }
    
    if (fast == NULL || fast->next == NULL) {
        return NULL;
    }

    slow = head;
    while (fast != slow) {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

解法2:哈希表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct hashTable {
    struct ListNode *key;
    UT_hash_handle hh;
};

struct hashTable *hashtable;

struct hashTable *find(struct ListNode *ikey) {
    struct hashTable *tmp;
    HASH_FIND_PTR(hashtable, &ikey, tmp);
    return tmp;
}

void insert(struct ListNode *ikey) {
    struct hashTable *tmp = malloc(sizeof(struct hashTable));
    tmp->key = ikey;
    HASH_ADD_PTR(hashtable, key, tmp);
}

struct ListNode* detectCycle(struct ListNode* head) {
    hashtable = NULL;
    while (head != NULL) {
        if (find(head) != NULL) {
            return head;
        }
        insert(head);
        head = head->next;
    }
    return false;
}

21. 合并两个有序链表

简单

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

示例 1:

img

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->val = -1;
    struct ListNode *new = dummy;
    if (list1 == NULL && list2 == NULL) {
        return NULL;
    }

    while(list1 != NULL && list2 != NULL) {
        if (list1->val < list2->val) {
            dummy->next = list1;
            list1 = list1->next;
        } else {
            dummy->next = list2;
            list2 = list2->next;
        }
        dummy = dummy->next;
    }

    if (list1 != NULL) {
        dummy->next = list1;
    }
    if (list2 != NULL) {
        dummy->next = list2;
    }

    return new->next;
}

2. 两数相加

中等

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

img

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {
    struct ListNode *head = NULL;
    struct ListNode *tail = NULL;
    int carry = 0;

    while (l1 || l2) {
        int n1 = l1 ? l1->val : 0;
        int n2 = l2 ? l2->val : 0;
        int sum = n1 + n2 + carry;
        carry = 0;
        if (!head) {
            tail = (struct ListNode*)malloc(sizeof(struct ListNode));
            tail->val = sum % 10;
            tail->next = NULL;
            head = tail;
        } else {
            tail->next = (struct ListNode*)malloc(sizeof(struct ListNode));
            tail->next->val = sum % 10;
            tail->next->next = NULL;
            tail = tail->next;

        }
        carry = sum / 10;
        if (l1) {
            l1 = l1->next;
        }
        if (l2) {
            l2 = l2->next;
        }
    }

    if (carry) {
        tail->next = (struct ListNode*)malloc(sizeof(struct ListNode));
        tail->next->val = carry;
        tail->next->next = NULL;
    }

    return head;
}

19. 删除链表的倒数第 N 个结点

中等

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

img

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

示例 2:

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

示例 3:

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

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

进阶:你能尝试使用一趟扫描实现吗?

解法1:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    if(NULL == head) {
        return NULL;
    }
    int cnt = 0;
    struct ListNode* tmp1 = (struct ListNode*)malloc(sizeof(struct ListNode));
    tmp1->val = 0;
    tmp1->next = head;
    struct ListNode* tmp2 = tmp1;
    while(head != NULL)
    {
        cnt++;      
        head = head->next;
    }
    if(cnt == 1) {
        return NULL;
    }

    for(int i = 1; i < cnt - n + 1; ++i)
    {
        tmp2 = tmp2->next;
    }
    tmp2->next = tmp2->next->next;
    return tmp1->next;
}

解法2:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->val = -1;
    dummy->next = head;
    struct ListNode *fast = dummy;
    struct ListNode *slow = dummy;
    
    for (int i = 0; i < n; i++) {
        fast = fast->next;
    }
    while (fast->next != NULL) {
        fast = fast->next;
        slow = slow->next;
    }
    slow->next = slow->next->next;

    return dummy->next;
}

24. 两两交换链表中的节点

中等

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

img

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

示例 2:

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

示例 3:

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

提示:

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

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* swapPairs(struct ListNode* head) {
    struct ListNode *dummy = (struct ListNode *)malloc(sizeof(struct ListNode));
    dummy->val = -1;
    dummy->next = head;
    struct ListNode *tmp = dummy;

    while (tmp->next != NULL && tmp->next->next != NULL) {
        struct ListNode *node1 = tmp->next;
        struct ListNode *node2 = tmp->next->next;
        tmp->next = node2;
        node1->next = node2->next;
        node2->next = node1;
        tmp = node1;
    }

    return dummy->next;
}

25. K 个一组翻转链表

困难

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

img

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

示例 2:

img

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

提示:

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
    struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->val = -1;
    dummy->next = NULL;
    struct ListNode *newHead = dummy;
    struct ListNode *newTail = NULL;
    struct ListNode *node = head;
    int count = 0;

    while (node != NULL) {
        count++;
        node = node->next;
    }
	
    // 每k个节点为一组,总共需要翻转count/k组,最后不足k个节点不需要翻转
    for (int i = count/k; i > 0; --i) {
        //翻转当前组中的节点,可以发现当前组的第一个节点是翻转后部分链表的最后一个节点
        newTail = head;
        //翻转k个节点,可以看作是链表的头插法
        for (int j = 0; j < k; j++) {
            node = head;
            head = head->next;
            node->next = newHead->next;
            newHead->next = node;
        }
        //当前组的节点翻转完成后,将已翻转部分的头节点赋值为尾节点,充当“dummy”的作用,为下一组节点翻转做准备
        newHead = newTail;
    }

    //所有组翻转完成,将剩下不足k的节点直接拼接到已翻转链表的尾部
    newTail->next = head;
    struct ListNode *ret = dummy->next;
    free(dummy);
    return ret;
}

138. 随机链表的复制

中等

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点

例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null

你的代码 接受原链表的头节点 head 作为传入参数。

示例 1:

img

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

img

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

示例 3:

img

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.randomnull 或指向链表中的节点。

解法1:

因为随机指针的存在,当我们拷贝节点时,当前节点随机指针指向的节点可能还没有创建。

哈希表+回溯

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
struct hashTable {
    struct Node *key, *val;
    UT_hash_handle hh;
};

struct hashTable *cacheNode;

struct Node* deepCopy(struct Node* head) {
    if (head == NULL) {
        return NULL;
    }
    struct hashTable *tmp;
    HASH_FIND_PTR(cacheNode, &head, tmp);
    if (tmp == NULL) {
        struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->val = head->val;
        tmp = (struct hashTable *)malloc(sizeof(struct hashTable));
        tmp->key = head;
        tmp->val = newNode;
        HASH_ADD_PTR(cacheNode, key, tmp);
        newNode->next = deepCopy(head->next);
        newNode->random = deepCopy(head->random);
    }
    return tmp->val;
}

struct Node* copyRandomList(struct Node* head) {
	cacheNode = NULL;
    return deepCopy(head);
}

时间复杂度O(n),空间复杂度O(n)

解法2:

迭代+节点拆分,参考官方题解解法二

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head) {
	if (head == NULL) {
        return NULL;
    }

    for (struct Node *node = head; node != NULL; node = node->next->next) {
        struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->val = node->val;
        newNode->next = node->next;
        node->next = newNode;
    }

    for (struct Node *node = head; node != NULL; node = node->next->next) {
        struct Node *newNode = node->next;
        newNode->random = (node->random != NULL) ? node->random->next : NULL;
    }

    struct Node *newHead = head->next;
    for (struct Node *node = head; node != NULL; node = node->next) {
        struct Node *newNode = node->next;
        node->next = node->next->next;
        newNode->next = (newNode->next != NULL) ? newNode->next->next : NULL;
    }

    return newHead; 
}

148. 排序链表

中等

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

示例 1:

img

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

示例 2:

img

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

示例 3:

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

提示:

  • 链表中节点的数目在范围 [0, 5 * 104]
  • -105 <= Node.val <= 105

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

解法:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *findMiddle(struct ListNode* head) {
    struct ListNode *slow = head;
    struct ListNode *fast = head->next;
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

struct ListNode *mergeTwoLists(struct ListNode *l1, struct ListNode *l2) {
    struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->val = -1;
    dummy->next = NULL;
    struct ListNode *cur = dummy;

    while (l1 != NULL && l2 != NULL) {
        if (l1->val < l2->val) {
            cur->next = l1;
            l1 = l1->next;
        } else {
            cur->next = l2;
            l2 = l2->next;
        }
        cur = cur->next;
    }

    cur->next = (l1 == NULL) ? l2 : l1;

    return dummy->next;
}

struct ListNode* sortList(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    struct ListNode *mid = findMiddle(head);
    struct ListNode *rightHead = mid->next;
    mid->next = NULL;
    struct ListNode *left = sortList(head);
    struct ListNode *right = sortList(rightHead);
    return mergeTwoLists(left, right);
}

23. 合并 K 个升序链表

困难

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoList(struct ListNode* l1, struct ListNode* l2) {
    struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->val = -1;
    dummy->next = NULL;
    struct ListNode *tmp = dummy;

    while (l1 != NULL && l2 != NULL) {
        if (l1->val < l2->val) {
            tmp->next = l1;
            l1 = l1->next;
        } else {
            tmp->next = l2;
            l2 = l2->next;
        }
        tmp = tmp->next;
    }

    tmp->next = (l1 == NULL) ? l2 : l1;
    return dummy->next;
}

struct ListNode* mergeKLists(struct ListNode** lists, int listsSize) {
    if (listsSize == 0) return NULL;
    if (listsSize == 1) return lists[0];

    int mid = listsSize / 2;
    struct ListNode *sub1[mid];
    struct ListNode *sub2[listsSize - mid];
    for (int i = 0; i < mid; ++i) {
        sub1[i] = lists[i];
    }
    for (int i = mid; i < listsSize; ++i) {
        sub2[i - mid] = lists[i];
    }

    return mergeTwoList(mergeKLists(sub1, mid), mergeKLists(sub2, listsSize - mid));
}

146. LRU 缓存

中等

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity)正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 getput 必须以 O(1) 的平均时间复杂度运行。

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 * 105getput

题解:




typedef struct {
    int key;
    int val;
    UT_hash_handle hh;
} LRUCache;

LRUCache *g_usr = NULL;
int g_size;

LRUCache* lRUCacheCreate(int capacity) {
    g_size = capacity;
    return g_usr;
}

int lRUCacheGet(LRUCache* obj, int key) {
    LRUCache *cur_usr = NULL;
    HASH_FIND_INT(g_usr, &key, cur_usr);
    if (cur_usr != NULL) {
        HASH_DEL(g_usr, cur_usr);
        HASH_ADD_INT(g_usr, key, cur_usr);
        return cur_usr->val;
    }
    return -1;
}

void lRUCachePut(LRUCache* obj, int key, int value) {
    LRUCache *cur_usr = NULL, *next_usr = NULL;
    HASH_FIND_INT(g_usr, &key, cur_usr);
    if (cur_usr != NULL) {
        HASH_DEL(g_usr, cur_usr);
        cur_usr->key = key;
        cur_usr->val = value;
        HASH_ADD_INT(g_usr, key, cur_usr);
    } else {
        int cnt = HASH_COUNT(g_usr);
        if (cnt == g_size) {
            HASH_ITER(hh, g_usr, cur_usr, next_usr) {
                HASH_DEL(g_usr, cur_usr);
                free(cur_usr);
                break;
            }
        }
        LRUCache *new_usr = (LRUCache*)malloc(sizeof(LRUCache));
        new_usr->key = key;
        new_usr->val = value;
        HASH_ADD_INT(g_usr, key, new_usr);
    }
}

void lRUCacheFree(LRUCache* obj) {
    LRUCache *cur_usr = NULL, *next_usr = NULL;
    HASH_ITER(hh, g_usr, cur_usr, next_usr) {
        HASH_DEL(g_usr, cur_usr);
        free(cur_usr);
    }
}

/**
 * Your LRUCache struct will be instantiated and called as such:
 * LRUCache* obj = lRUCacheCreate(capacity);
 * int param_1 = lRUCacheGet(obj, key);
 
 * lRUCachePut(obj, key, value);
 
 * lRUCacheFree(obj);
*/

注意:未使用obj传递

posted @ 2025-02-15 19:52  调蓝师  阅读(96)  评论(0)    收藏  举报