leetcode-2.两数之和
leetcode-2.两数之和
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
/**
* 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, * tail = NULL;
int carry = 0;
while(l1 || l2){
int a = l1 ? l1->val : 0;
int b = l2 ? l2->val : 0;
if(!head){
head = tail = malloc(sizeof(struct ListNode));
tail->val = (a + b + carry) % 10;
tail->next = NULL;
}
else{
tail->next = malloc(sizeof(struct ListNode));
tail->next->val = (a + b + carry) % 10;
tail = tail-> next;
tail->next = NULL;
}
carry = (a + b + carry) / 10;
if(l1){
l1 = l1->next;
}
if(l2){
l2 = l2->next;
}
}
if(carry > 0){
tail->next = malloc(sizeof(struct ListNode));
tail->next->val = carry;
tail = tail->next;
tail->next = NULL;
}
return head;
}
leetcode-19.删除链表的倒数第N个结点
leetcode-19.删除链表的倒数第N个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* headNode = (struct ListNode*)malloc(sizeof(struct ListNode));
headNode->val = 0;
headNode->next = head;
struct ListNode* fast = head;
struct ListNode* slow = headNode;
int len = 0;
while(len < n){
len++;
fast = fast->next;
}
while(fast != NULL){
slow = slow->next;
fast = fast->next;
}
slow->next = slow->next->next;
struct ListNode* ret = headNode->next;
free(headNode);
return ret;
}
时间复杂度:O(L),其中L是链表的长度。
空间复杂度:O(1)。
leetcode-21.合并两个有序链表
leetcode-21.合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
方法一:带头结点
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if(list1 == NULL) return list2;
if(list2 == NULL) return list1;
struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* tail = head;
while(list1 != NULL && list2 != NULL){
if(list1->val < list2->val){
tail->next = list1;
list1 = list1->next;
}
else{
tail->next = list2;
list2 = list2->next;
}
tail = tail->next;
}
if(list1 != NULL) tail->next = list1;
else tail->next = list2;
struct ListNode* first = head->next;
free(head);
return first;
}
方法二:不带头结点
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if(list1 == NULL) return list2;
if(list2 == NULL) return list1;
struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
if(list1->val < list2->val){
head = list1;
list1 = list1->next;
}
else{
head = list2;
list2 = list2->next;
}
struct ListNode* tail = head;
while(list1 != NULL && list2 != NULL){
if(list1->val < list2->val){
tail->next = list1;
list1 = list1->next;
}
else{
tail->next = list2;
list2 = list2->next;
}
tail = tail->next;
}
if(list1 != NULL) tail->next = list1;
else tail->next = list2;
return head;
}
时间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度。因为每次循环迭代中,list1 和 list2 只有一个元素会被放进合并链表中, 因此 while 循环的次数不会超过两个链表的长度之和。所有其他操作的时间复杂度都是常数级别的,因此总的时间复杂度为 O(n + m)。
空间复杂度:O(1)。我们只需要常数的空间存放若干变量。
leetcode-24.两两交换链表中的节点
leetcode-24.两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
struct ListNode* swapPairs(struct ListNode* head){
struct ListNode* swapList = (struct ListNode*)malloc(sizeof(struct ListNode));
swapList->next = head;
struct ListNode* temp = swapList;
while(temp->next != NULL && temp->next->next != NULL){
struct ListNode* node1 = temp->next;
struct ListNode* node2 = temp->next->next;
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
return swapList->next;
}
时间复杂度:O(n),其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。
空间复杂度:O(1)。
leetcode-61.循环链表
leetcode-61.循环链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
struct ListNode* rotateRight(struct ListNode* head, int k){
if(head == NULL)
return NULL;
struct ListNode* p = head;
int n = 1; // 用于计算链表长度
while(p->next != NULL){
p = p->next;
n++;
}
p->next = head; // head 变为循环链表
int m = n - k % n; // 计算移位后头节点的前一个节点
while(m > 0){
p = p-> next;
m--;
}
head = p->next; // 将移位后的头节点赋给 head
p->next = NULL; // 断开循环链表
return head;
}
时间复杂度:O(n)。
空间复杂度:O(1)。
leetcode-82.删除排序链表中的重复元素 II
leetcode-82.删除排序链表中的重复元素 II
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
if(!head){
return head;
}
struct ListNode* h = malloc(sizeof(struct ListNode));
h->next = head;
struct ListNode* cur = h;
while(cur->next && cur->next->next){
if(cur->next->val == cur->next->next->val){
int x = cur->next->val;
while(cur->next && cur->next->val == x){
cur->next = cur->next->next;
}
}
else{
cur = cur->next;
}
}
return h->next;
}
时间复杂度:O(n),其中 n 是链表的长度。
空间复杂度:O(1)。
leetcode-83.删除排序链表中的重复元素
leetcode-83.删除排序链表中的重复元素
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
if(!head){
return head;
}
struct ListNode* cur = malloc(sizeof(struct ListNode));
cur = head;
while(cur->next){
if(cur->val == cur->next->val){
cur->next = cur->next->next;
}
else{
cur = cur->next;
}
}
return head;
}
时间复杂度:O(n),其中 n 是链表的长度。
空间复杂度:O(1)。
leetcode-86.分隔链表
leetcode-86.分隔链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* partition(struct ListNode* head, int x){
struct ListNode* small = malloc(sizeof(struct ListNode));
struct ListNode* smallHead = small;
struct ListNode* large = malloc(sizeof(struct ListNode));
struct ListNode* largeHead = large;
while(head != NULL){
if(head->val < x){
small->next = head;
small = small->next;
}
else{
large->next = head;
large = large->next;
}
head = head->next;
}
large->next = NULL;
small->next = largeHead->next;
return smallHead->next;
}
时间复杂度:O(n),其中 n 是原链表的长度。我们对该链表进行了一次遍历。
空间复杂度:O(1)。
leetcode-92.反转链表 II
leetcode-92.反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
struct ListNode* reverseBetween(struct ListNode* head, int left, int right){
struct ListNode* node = malloc(sizeof(struct ListNode));
node->next = head;
struct ListNode* pre = malloc(sizeof(struct ListNode));
pre = node;
int t = 1;
while(t < left){
pre = pre->next;
t++;
}
struct ListNode* cur = pre->next;
struct ListNode* l;
while(t < right){
l = cur->next;
cur->next = cur->next->next;
l->next = pre->next;
pre->next = l;
t++;
}
return node->next;
}
时间复杂度:O(n),其中 n 是链表总节点数。最多只遍历了链表一次,就完成了反转。
空间复杂度:O(1)。只使用到常数个变量。
leetcode-141.环形链表
leetcode-141.环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
bool hasCycle(struct ListNode *head) {
if(head == NULL || head->next == NULL)
return false;
struct ListNode* slow = head;
struct ListNode* fast = head->next;
while(slow != fast){
if(fast == NULL || fast->next == NULL)
return false;
slow = slow->next;
fast = fast->next->next;
}
return true;
}
时间复杂度:O(N),其中 N 是链表中的节点数。
- 当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。
- 当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮。
空间复杂度:O(1)。我们只使用了两个指针的额外空间。
leetcode-142.环形链表 II
leetcode-142.环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast != NULL){
slow = slow->next;
if(fast->next == NULL)
return NULL;
fast = fast->next->next;
if(slow == fast){
struct ListNode* ptr = head;
while(ptr != slow){
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return NULL;
}
时间复杂度:O(N),其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)。
空间复杂度:O(1)。我们只使用了 slow,fast,ptr 三个指针。
leetcode-143.重排链表
// 找中间节点
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast->next != NULL && fast->next->next != NULL){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 反转链表
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* prev = NULL;
struct ListNode* nxt = head;
while(nxt != NULL){
struct ListNode* nxtTmp = nxt->next;
nxt->next = prev;
prev = nxt;
nxt = nxtTmp;
}
return prev;
}
// 合并链表
void mergeList(struct ListNode* l1, struct ListNode* l2)
{
struct ListNode* l1_tmp;
struct ListNode* l2_tmp;
while(l1 != NULL && l2 != NULL){
l1_tmp = l1->next;
l2_tmp = l2->next;
l1->next = l2;
l1 = l1_tmp;
l2->next = l1;
l2 = l2_tmp;
}
}
void reorderList(struct ListNode* head){
if(head == NULL)
return ;
struct ListNode* mid = middleNode(head);
struct ListNode* left = head;
struct ListNode* right = mid->next;
mid->next = NULL;
right = reverseList(right);
mergeList(left, right);
}
时间复杂度:O(N),其中 N 是链表中的节点数。
空间复杂度:O(1)。
leetcode-147.对链表进行插入排序
leetcode-147.对链表进行插入排序
给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
struct ListNode* insertionSortList(struct ListNode* head){
if(head == NULL){
return head;
}
struct ListNode* dummyHead = malloc(sizeof(struct ListNode));
dummyHead->val = 0;
dummyHead->next = head;
struct ListNode* lastSorted = head;
struct ListNode* curr = head->next;
while(curr != NULL){
if(lastSorted->val <= curr->val){
lastSorted = lastSorted->next;
}
else{
struct ListNode* prev = dummyHead;
while(prev->next->val <= curr->val){
prev = prev->next;
}
lastSorted->next = curr->next;
curr->next = prev->next;
prev->next = curr;
}
curr = lastSorted->next;
}
return dummyHead->next;
}
时间复杂度: O(n2),其中 n 是链表的长度。
空间复杂度:O(1)。
leetcode-160.相交链表
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if(headA == NULL || headB == NULL){
return NULL;
}
struct ListNode* la = headA;
struct ListNode* lb = headB;
while(la != lb){
la = la == NULL ? headB : la->next;
lb = lb == NULL ? headA : lb->next;
}
return la;
}
若相交,链表A: a+c, 链表B : b+c,a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。
若不相交,a+b = b+a 。因此相遇处是NULL。
时间复杂度:O(m+n),其中 m 和 n 是分别是链表 headA 和 headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。
空间复杂度:O(1)。
leetcode-203.移出链表元素
leetcode-203.移出链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
struct ListNode* removeElements(struct ListNode* head, int val){
if(head == NULL){
return NULL;
}
struct ListNode* node = malloc(sizeof(struct ListNode));
node->next = head;
struct ListNode* pre = node;
while(pre->next != NULL){
if(pre->next->val == val){
pre->next = pre->next->next;
}
else{
pre = pre->next;
}
}
return node->next;
}
时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
空间复杂度:O(1)。
leetcode-206.反转链表
leetcode-206.反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* node1 = NULL;
struct ListNode* node2 = head;
while(node2 != NULL){
struct ListNode* nxt = node2->next;
node2->next = node1;
node1 = node2;
node2 = nxt;
}
return node1;
}
时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
空间复杂度:O(1)。
leetcode-234.回文链表
leetcode-234.回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* prev = NULL;
struct ListNode* curr = head;
while(curr != NULL){
struct ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
struct ListNode* endOfFirstHalf(struct ListNode* head)
{
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast->next != NULL && fast->next->next != NULL){
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
bool isPalindrome(struct ListNode* head){
if(head == NULL){
return true;
}
// 找到前半部分链表的尾结点并反转后半部分链表
struct ListNode* firstHalfEnd = endOfFirstHalf(head);
struct ListNode* secondHalfStart = reverseList(firstHalfEnd->next);
// 判断是否回文
struct ListNode* p1 = head;
struct ListNode* p2 = secondHalfStart;
bool res = true;
while(res && p2 != NULL){
if(p1->val != p2->val){
res = false;
}
p1 = p1->next;
p2 = p2->next;
}
// 还原链表并返回结果
firstHalfEnd->next = reverseList(secondHalfStart);
return res;
}
时间复杂度:O(n),其中 n 指的是链表的大小。
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)。
leetcode-237.删除链表中的节点
leetcode-237.删除链表中的节点
有一个单链表的 head,我们想删除它其中的一个节点 node。
给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。
链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。
删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
给定节点的值不应该存在于链表中。
链表中的节点数应该减少 1。
node 前面的所有值顺序相同。
node 后面的所有值顺序相同。
void deleteNode(struct ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
时间复杂度:O(1)。
空间复杂度:O(1)。
leetcode-328.奇偶链表
leetcode-328.奇偶链表
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
struct ListNode* oddEvenList(struct ListNode* head){
if(head == NULL){
return NULL;
}
struct ListNode* evenhead = head->next;
struct ListNode* odd = head;
struct ListNode* even = evenhead;
while(even != NULL && even->next != NULL){
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
odd->next = evenhead;
return head;
}
时间复杂度:O(n),其中 n 是链表的节点数。需要遍历链表中的每个节点,并更新指针。
空间复杂度:O(1)。只需要维护有限的指针。