点击蓝字
关注我们
Contents-
1 找出两个链表的交点 (160)
2 链表反转 (206)
3 归并两个有序的链表 (21)
4 从有序链表中删除重复节点(83)
5 删除链表的倒数第 n 个节点(19)
6 交换链表中的相邻结点 (24)
7 链表求和(445)
8 回文链表(234)
9 分隔链表(725)
10 链表元素按奇偶聚集(328)
1 找出两个链表的交点 (160)
题目:编写一个程序,找到两个单链表相交的起始节点。 思路1:此题的目的是判断两个链表共同拥有的部分的起始地址。首先将不等长的链表从尾部对齐,这样就保证了最后的共同拥有的部分已经对齐,之后只需将较长的链表先移动与和短的链表平齐,然后一起向后遍历,找到结点地址相等的节点返回即可。可以自己定义一个获取链表长度的函数length()。class Solution {public: int length(ListNode *A){ int len = 0; while(A->next != NULL){ len++; A = A->next; } return len; } ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { ListNode *A = headA; ListNode *B = headB; if(A==NULL||B==NULL) return NULL; int len_a = length(headA); int len_b = length(headB); for(; len_a B = B->next; } for(; len_a>len_b; len_a--){ A = A->next; } while(A != NULL && A!=B){ A = A->next; B = B->next; } return A; }};
思路2:设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。 当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。 如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == NULL || headB ==NULL){
return NULL;
}
ListNode* A = headA;
ListNode* B = headB;
while( A != B){
A = A == NULL? headA:A->next;
B = B == NULL? headB:B->next;
}
return A;
}
};
两个思路的对比:
通过结果可以看出两个思路内存消耗相同的情况下,思路2在运行时间上要大得多。
2 链表反转 (206)
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 思路: 涉及到链表反转等改变链表的连接结构时, 不可以用 pre=pre->next 来向前推进节点 因为已经修改了链表的原有顺序 !!!需用 ListNode* temp = pre->next;来时刻记录pre-next的节点。class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = NULL;
ListNode* pre = head;
if(head == NULL) return head;
while(pre != NULL){
ListNode* temp = pre->next;
pre->next = cur;
cur = pre;
pre = temp;
}
return cur;
}
};
3 归并两个有序的链表 (21)
将两个升序链表合并为一个新的 升序(非降) 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 思路: 首先任意定义一个头节点result,只是起到一个对之后排序的节点的领头作用,最后返回时仅返回result->next即可。- 定义头结点
- 若 l1 指向的结点值 >= l2 指向的结点值,则将 l1 链接到头结点的 next 位置; 否则将 l2 链接到头结点的 next 位置;
- 循环进行,直至 l1 或 l2 为 NULL;
- 最后,将 l1 或 l2 中剩余部分,链接到头结点后面;
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* result = new ListNode(1);
ListNode* end = result;
while(l1 != nullptr && l2 != nullptr){
if(l1->val >= l2->val){
result->next = l2;
l2 = l2->next;
}
else{
result->next = l1;
l1 = l1->next;
}
result = result->next;
}
result->next = l1==nullptr? l2:l1;
return end->next;
}
};
4 从有序链表中删除重复节点(83)
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。 示例: 输入: 1->1->2->3->3 输出: 1->2->3 思路:定义两个节点指针,通过比较节点大小不断修改第一个指针的后继,直到第一个指针的后继为NULL;要注意后继节点succ->next = NULL的情况。class Solution {public: ListNode* deleteDuplicates(ListNode* head) { if(head == NULL || head->next == NULL) return head; ListNode* cur = head; ListNode* succ = head->next; while(cur->next != NULL){ if(cur->val == succ->val){ if(succ->next ==NULL){ cur->next = NULL; } else{ cur->next = succ->next; // 将当前节点的后继修改为重复元素节点的后继,相当于删掉了重复节点succ; succ = succ->next; } } else{ cur = cur->next; succ = succ->next; } } return head; }};
5 删除链表的倒数第 n 个节点(19)
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 1->2->3->5. 思路:首先获取链表长度,根据n可知道删除节点之前的节点个数,将待删除节点之前的节点和之后的节点连接即可。注:若链表长度和n相同,即删除链表的第一个节点,直接返回head->next即可。class Solution {
public:
int length(ListNode* A){
int len = 0;
while(A != NULL){
len++;
A = A->next;
}
return len;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head == NULL) return head;
if(head->next == NULL && n ==1) return head->next;
ListNode* result = head;
int len = length(head);
if(len == n){
return result->next;
}
for(int i=1; i
head = head->next;
}
head->next = head->next->next;
head = head->next;
while(head!=NULL){
for(int i=0; i1; i++){
head = head->next;
}
}
return result;
}
};
6 交换链表中的相邻结点 (24)
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例: 输入: 1->2->3->4 输出: 2->1->4->3. 思路:head节点以长度2向前推进,需要借助一个辅助头节点来指向head,终止条件为head == NULL || head->next == NULL,分别对应于链表节点个数为偶数和奇数的情况。class Solution {public: ListNode* swapPairs(ListNode* head) { //新建一个空结点,用来指向头节点 ListNode* p = new ListNode(0); p->next = head; //新建和p相同一个curr节点,两个相同的节点一个是当前做改变的节点,一个是保持不动用来返回的节点 ListNode* curr = p; //循环条件为当前节点为NULL或当前的下一个节点为NULL,分别对应偶数和奇数个节点的终止标志 while(head != NULL && head->next != NULL) { //为了清晰明了,我们新建两个节点,一节点和二节点 ListNode* firstNode = head; ListNode* secondNode = head->next; ///把一和二进行交换,并连接前后 //当前curr节点指向二节点 curr->next = secondNode; //一节点指向二节点此时的下一节点 firstNode->next = secondNode->next; //二节点指向一节点,即交换位置成功 secondNode->next = firstNode; //由于每次循环curr节点都指向每次循环的一节点,所以要再次把curr节点指向一节点 curr = firstNode; //每次移动都是由head节点来赋值操作,所以head应向右移动两格,即新循环的一节点 head = firstNode->next; } //返回p的下一个节点即对应整个操作后的链表 return p->next; }};
7 链表求和(445)
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。不能对列表中的节点进行翻转。 示例: 输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 8 -> 0 -> 7 思路:class Solution {public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { int count=0, temp; ListNode* head, *last; for(head = l1; head!=NULL;head=head->next) count++; for(head = l2; head!=NULL;head=head->next) count--; if(count<0) swap(l1, l2); //last = new ListNode(0); last = head = new ListNode(0); // last 和 head指向同节点,且该节点值为0 如果分来new,则两个节点指针指向地址不同,会报错。 head->next = l1; for(int i=abs(count); i!=0; i--){ if(l1->val!=9) last=l1; l1 = l1->next; } while(l1!=NULL){ temp = l1->val + l2->val; if(temp>9){ temp -= 10; last->val += 1; last = last->next; while(last != l1){ if(last!=NULL) last->val =0; last = last->next; } } else if(temp != 9) last = l1; l1->val = temp; l1 = l1->next; l2 = l2->next; } return head->val ==1? head:head->next; }};
8 回文链表(234)
请判断一个链表是否为回文链表。 实例: 输入: 1->2->2->1 输出: true 思路1:将链表的值依次存入向量中,然后直接按秩取值进行回文判断; 思路2:将链表后半部分进行反转,链表的中间节点为两边两个子链表的共同节点,然后两个子链表按序遍历比较即可。class Solution {public: bool isPalindrome(ListNode* head) { // 时间复杂度、空间复杂度均为o(n) vector a; while(head){ a.push_back(head->val); head = head->next; } for(int i =0; i if(a[i]!=a[a.size()-i-1]) return false; } return true; /* 链表反转后半部分,再逐一比较 if(head==NULL ||head->next==NULL) return true; ListNode* slow = head; ListNode* fast = head; //找到链表中间节点 while(fast!=NULL && fast->next!=NULL){ slow = slow->next; fast = fast->next->next; } ListNode* curr = slow; ListNode* nextNode = curr->next; //反转后半部分链表 while(nextNode!=NULL){ ListNode* temp = nextNode->next; nextNode->next = curr; curr = nextNode; nextNode = temp; } slow->next = NULL; //开始逐一比较 while(head!=NULL && curr!=NULL){ if(head->val != curr->val) return false; head = head->next; curr = curr->next; } return true; */ }};
9 分隔链表(725)
给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。 每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。 这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。 返回一个符合上述规则的链表的列表。 示例: 输入:1->2->3->4, k = 5 // 5 输出: [ [1], [2], [3], [4], null ] 思路:首先得到链表长度,若然后求出每个子链表应有的长度,保留余数,将余数分散到结果列表中的前几个链表当中;在每个子链表到达其长度后将子链表最后一个节点指向NULL,使得与原链表断开。class Solution {
public:
vector splitListToParts(ListNode* root, int k) {
if(root==NULL) return vector(k, NULL);
ListNode* curr = root;
int len=0;
while(curr!=NULL){
len++;
curr = curr->next;
}
int avglen = len / k;
int res = len % k;
vector result(k, NULL);
ListNode* now = root;
ListNode* prev = NULL;
for(int i=0; i
result[i] = now;
int eachlen = res>0? avglen+1:avglen;
for(int j=0; j// 内层循环处理result的每一位应该包含原始链表的哪些元素
prev = now;
now = now->next;
}
prev->next = NULL;
res--;
}
return result;
}
};
10 链表元素按奇偶聚集(328)
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。 示例 : 输入: 2->1->3->5->6->4->7->NULL 输出: 2->3->6->7->1->5->4->NULL思路:class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
if(!head) return head;
ListNode* odd = head;
ListNode * even = head->next;
ListNode * evenstart = even;
while(even&&even->next){
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
odd->next = evenstart;
return head;
}
};
写留言