一、反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路一:翻转单链表指针方向
这里解释一下三个指针的作用:
n1:记录上一个节点,如果是第一个就指向空
n2:记录此节点的位置
n3:记录下一个节点的位置,让翻转后能找到下一个节点,防止丢失指针的地址
/* * 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* n1 = NULL,*n2 = head,*n3 = n2->next; //结束条件 while(n2) { //迭代过程 n2->next = n1; n1 = n2; n2 = n3; if(n3) n3 = n3->next; } return n1; }
思路二:头插法
取原链表节点头插到新链表
/* * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* reverseList(struct ListNode* head) { struct ListNode* cur = head; struct ListNode* newHead = NULL; while(cur) { struct ListNode* next = cur->next; //头插 cur->next = newHead; newHead = cur; cur = next; } return newHead; }
二、链表的中间结点
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路一:单指针法
- 时间复杂度:O(N*1.5),其中 N 是给定链表的结点数目。
- 空间复杂度:O(1),只需要常数空间存放变量和指针。
我们可以对链表进行两次遍历。第一次遍历时,我们统计链表中的元素个数 N;第二次遍历时,我们遍历到第 N/2 个元素(链表的首节点为第 0 个元素)时,将该元素返回即可。
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* middleNode(struct ListNode* head) { int n = 0; struct ListNode*cur = head; while(cur != NULL) { ++n; cur = cur->next; } int k = 0; cur = head; while(k < n/2) { ++k; cur = cur->next; } return cur; }
思路二:快慢指针法
- 时间复杂度:O(N),其中 N 是给定链表的结点数目。
- 空间复杂度:O(1),只需要常数空间存放 slow 和 fast 两个指针。
我们可以优化思路一,用两个指针 slow 与 fast 一起遍历链表。slow 一次走一步,fast 一次走两步。那么当 fast 到达链表的末尾时,slow 必然位于中间。
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* middleNode(struct ListNode* head) { struct ListNode* slow = head,*fast = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; }
三、合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路一(迭代法):
定义一个头指针和一个尾指针,从小到大依次尾插,直到一个链表为空时结束
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) { if(l1 == NULL) return l2; if(l2 == NULL) return l1; struct ListNode* head = NULL, *tail = NULL; while(l1 != NULL && l2 != NULL) { if(l1->val < l2->val) { //尾插 if(tail == NULL) { head = tail = l1; } else{ tail->next = l1; tail = tail->next; } l1 = l1->next; } else{ if(tail == NULL) { head = tail = l2; } else{ tail->next = l2; tail = tail->next; } l2 = l2->next; } } if(l1) tail->next= l1; if(l2) tail->next= l2; return head; }
优化一:
先确定头结点,然后再循环判断val大小,尾插
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) { if(l1 == NULL) return l2; if(l2 == NULL) return l1; struct ListNode* head = NULL, *tail = NULL; //先确定头节点 if(l1->val < l2->val) { head = tail =l1; l1 = l1->next; }else{ head = tail =l2; l2 = l2->next; } while(l1 && l2) { //尾插 if(l1->val < l2->val) { tail->next = l1; l1 = l1->next; } else{ tail->next = l2; l2 = l2->next; } tail = tail->next; } if(l1) tail->next= l1; if(l2) tail->next= l2; return head; }
优化二:
设置一个哨兵位的头节点,然后再去尾插。
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) { if(l1 == NULL) return l2; if(l2 == NULL) return l1; struct ListNode* head = NULL, *tail = NULL; //哨兵位的头节点 head = tail = (struct ListNode*)malloc(sizeof(struct ListNode)); while(l1 && l2) { //尾插 if(l1->val < l2->val) { tail->next = l1; l1 = l1->next; } else{ tail->next = l2; l2 = l2->next; } tail = tail->next; } if(l1) tail->next= l1; if(l2) tail->next= l2; struct ListNode* first = head->next; free(head); return first; }
思路二(递归法):
(这是题解中大佬的一个解法)以迭代的思路写递归,尤为惊人!!!
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){ /*if判断: 1.如果l1为空,返回l2 2.如果l2为空,返回l1 3.如果l1的值小于l2,比较l1的next值和l2,并把值赋给l1的下一个;返回l1 4.反之,比较l1和l2的next值,并把值赋给l2的下一个;返回l2 */ if (l1 == NULL) { return l2; } else if (l2 == NULL) { return l1; } else if (l1->val < l2->val) { l1->next = mergeTwoLists(l1->next, l2); return l1; } else { l2->next = mergeTwoLists(l1, l2->next); return l2; } }
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。
如果不做,可能导致读写文件的问题。
今天就先到这了!!!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信!!!