算法练习——链表
一:两数相加
题目要求:
解题思路:
思路:注意进位即可
实现代码:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* cur1 = l1, * cur2 = l2;
ListNode* phead = new ListNode(0);
ListNode* cur = phead;
int sum = 0;
while(cur1 || cur2 || sum) {//除了两个链表中的数外,还要考虑进位,只要存在进位就要加
if(cur1) {
sum += cur1->val;
cur1 = cur1->next;
}
if(cur2) {
sum += cur2->val;
cur2 = cur2->next;
}
cur->next = new ListNode(sum%10);
cur = cur->next;
sum /= 10;
}
ListNode* tmp = phead->next;
delete phead;
return tmp;
}
二:两两交换链表中的节点
题目要求:
解题思路:
思路:通过两个指针遍历链表,遍历的同时,将指针指向的节点按照顺序头插入新的节点中
实现代码:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr) {
return head;
}
ListNode* prev = head, *cur = head->next;
ListNode* phead = new ListNode(0);
ListNode* tmp = phead;
while(prev && cur) {
tmp->next = new ListNode(cur->val);
tmp = tmp->next;
tmp->next = new ListNode(prev->val);
tmp = tmp->next;
prev = cur->next;
if(prev) {
cur = prev->next;
}
}
tmp->next = prev;
tmp = phead->next;
delete phead;
return tmp;
}
三:重排链表
题目要求:
解题思路:
思路:
①通过快慢指针找到中间节点,此时慢指针将整个链表分成两段
②断开前一段,以及翻转后一段
③合并两个链表
实现代码:
ListNode * Reverse(ListNode * head) {
ListNode* phead = nullptr;
ListNode* cur = head;
while (cur) {
ListNode* next = cur->next;
cur->next = phead;
phead = cur;
cur = next;
}
return phead;
}
//思路:找到中间节点并翻转,然后合并两个链表
void reorderList(ListNode * head) {
//排除边界条件
if (head->next == nullptr || head->next->next == nullptr) {
return;
}
//找中间节点
ListNode* fast = head,* slow = head,* tmp = head;
while (fast && fast->next) {
fast = fast->next->next;
tmp = slow;
slow = slow->next;
}
//断开前一个链表
tmp->next = nullptr;
ListNode* cur1 = head;
ListNode* cur2 = Reverse(slow);//翻转第二个链表
ListNode* phead = new ListNode(0);
ListNode* cur = phead;
//合并两个链表
while (cur1 || cur2) {
if(cur1) {
ListNode* next = cur1->next;
cur->next = cur1;
cur = cur1;
cur1 = next;
}
if(cur2) {
ListNode* next = cur2->next;
cur->next = cur2;
cur = cur2;
cur2 = next;
}
}
head = phead->next;;
delete phead;
}
四:合并K个升序链表
题目要求:
解题思路:
思路:借助优先级队列来辅助解题
①将vector中,链表的头节点建成小堆,此时堆顶数据为最小值
②取堆顶数据,并用变量cur记录,再出堆顶数据,此时cur为vector中,某一链表的头节点
③将cur链接到一个新的链表中,同时判断cur->next 节点是否为空,若不为空,将其插入堆中,重新建堆,保证堆顶始终是最小值,当堆为空时,表示已经遍历完所有链表此时循环结束。
实现代码:
struct com {
//vector中默认的排序不满足题目要求,所以得自己实现
bool operator()(const ListNode* x1, const ListNode* x2) {
return x1->val > x2->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*,vector<ListNode*>,com> heap;
//将vector中,每个链表的头节点建堆
for(auto l : lists) {
if(l) heap.push(l);
}
ListNode* phead = new ListNode(0);
ListNode* cur = phead;
while(!heap.empty()) {
cur->next = heap.top();//堆顶元素其实就是一个节点,且该节点为最小值,赋值给cur
heap.pop();//出堆顶,避免重复
cur = cur->next;
if(cur->next) {//如果vector中 某个链表仍存在下一个节点,则插入进堆中,重新建堆,确保堆顶仍为最小节点
heap.push(cur->next);
}
}
return phead->next;
}
五:K个一组翻转链表
题目要求:
解题思路:
思路:
①:遍历链表,统计一个有几个节点total,n = total / k 得到需要翻转n次
②:创建一个链表头phead,定义两个链表变量cur、prev,令 cur = head, prev = phead; 创建一个链表遍历 next = cur->next;如图所示:
③:通过两次循环,外循环为总共翻转的次数,内循环为每次翻转时,需要翻转的数的个数。在进行内循环前,定义一个链表遍历 tmp = cur,如图所示:
④:在内循环中,我们进行头插操作
for(int j = 0; j < k; j++) { ListNode* next = cur->next; cur->next = prev->next; prev->next = cur; cur = next; {
当内循环一轮结束时,此时指针指向如图所示:
⑤:此时我们需将prev更新至tmp位置,以满足下一次循环能够进行头插操作,下一次内循环开始前,又会将tmo更新至cur位置,这样第二次循环结束时,同样将prev的位置更新至tmp,开始第三次头插,直至外循环结束。如图所示
⑥:当外循环结束时,此时判断cur是否为空,若不为空,则将后续数据链接到prev后面。
实现代码:
ListNode* reverseKGroup(ListNode* head, int k) {
if(head->next == nullptr) {
return head;
}
ListNode* cur = head;
int total = 0;
while(cur) {
total++;
cur = cur->next;
}
int n = total / k;
ListNode* phead = new ListNode(0);
cur = head;
ListNode *prev = phead;
for(int i = 0; i < n; i++) {
ListNode* tmp = cur;
for(int j = 0; j < k; j++) {
ListNode* next = cur->next;
cur->next = prev->next;
prev->next = cur;
cur = next;
}
prev = tmp;
}
if(cur) {
prev->next = cur;
}
ListNode* ret = phead->next;
delete phead;
return ret;
}
原文地址:https://blog.csdn.net/m0_51952310/article/details/145202693
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!