两个链表从头加到尾,边加边创建新链表的节点,最后有进位则再创建一个节点
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* head=new ListNode(-1);
ListNode* h=head;
int flag=0;
while(l1!=NULL||l2!=NULL){
int sum=0;
if(l1!=NULL) {
sum+=l1->val;
l1=l1->next;
}
if(l2!=NULL){
sum+=l2->val;
l2=l2->next;
}
sum+=flag;
h->next=new ListNode(sum%10);
h=h->next;
flag=sum/10;
}
if (flag) {
h->next=new ListNode(1);
h=h->next;
}
return head->next;
}
碰到逆序操作的题,考虑先全部入栈再相加
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<int> s1, s2;
while (l1) {
s1.push(l1 -> val);
l1 = l1 -> next;
}
while (l2) {
s2.push(l2 -> val);
l2 = l2 -> next;
}
int carry = 0;
ListNode* ans = nullptr;
while (!s1.empty() or !s2.empty() or carry != 0) {
int a = s1.empty() ? 0 : s1.top();
int b = s2.empty() ? 0 : s2.top();
if (!s1.empty()) s1.pop();
if (!s2.empty()) s2.pop();
int cur = a + b + carry;
carry = cur / 10;
cur %= 10;
auto curnode = new ListNode(cur);
curnode -> next = ans;
ans = curnode;
}
return ans;
}
};
1、用快慢指针,找倒数第n个,等于让快指针先走n步,再和慢指针同时走到尾节点
2、此时慢指针指向第n个节点,删除该节点即可
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//链表题常用技巧,定义哑节点指向头结点
ListNode* dummy = new ListNode(-1, head);
ListNode* first = dummy;ListNode* second = dummy;
//由于不知道链表总体长度,定义快慢指针
for (int i = 0; i < n; i++)
first = first->next;
while (first->next) {
first = first->next;
second = second->next;
}
second->next = second->next->next;
return head;
}
ListNode* getKthFromEnd(ListNode* head, int k) {
//指针模板体型,双指针,用dummy开头,返回dummy->next
ListNode* dummy=new ListNode(-1);
dummy->next=head;
ListNode* first=dummy;
ListNode* second=dummy;
for(int i=0;i<k;i++)
second=second->next;
while(second->next!=NULL){
first=first->next;
second=second->next;
}
return first->next;
}
利用函数返回为链表节点,进行递归调用。
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL)
return l2;
if (l2 == NULL)
return l1;
if (l1->val < l2->val){
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else{
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
写好一个合并函数,然后调用n次合并操作即可
class Solution {
public:
ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
if ((!a) || (!b)) return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr && bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr; aPtr = aPtr->next;
} else {
tail->next = bPtr; bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *ans = nullptr;
for (size_t i = 0; i < lists.size(); ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
};
递归方法修改,每次只修改当前两个节点
ListNode* swapPairs(ListNode* head) {
if(head==nullptr||head->next==nullptr)
return head;
ListNode* node=head->next;
head->next=swapPairs(node->next);
node->next=head;
return node;
}
1、k个一组进行翻转链表,写好一个翻转链表函数,移动head进行判断
2、使用伪头结点hair避免边界判断,最终答案无视head,返回hair->next
3、遍历时记录head的前一个节点pre和tail后一个节点nex,翻转完接回去
3、若剩余节点不足k个,则直接返回hair->next
// 翻转一个子链表,并且返回新的头与尾
pair<ListNode*, ListNode*> myReverse(ListNode* head, ListNode* tail) {
ListNode* prev = tail->next;
ListNode* p = head;
while (prev != tail) {
ListNode* nex = p->next;
p->next = prev;
prev = p;
p = nex;
}
return {tail, head};
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* hair = new ListNode(0);
hair->next = head;
ListNode* pre = hair;
while (head) {
ListNode* tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail->next;
if (!tail) {
return hair->next;
}
}
ListNode* nex = tail->next;
pair<ListNode*, ListNode*> result = myReverse(head, tail);
head = result.first;
tail = result.second;
// 把子链表重新接回原链表
pre->next = head;
tail->next = nex;
pre = tail;
head = tail->next;
}
return hair->next;
}
简易递归版
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if (head == NULL) return NULL;
ListNode *a = head;
ListNode *b = head;
for (int i = 0; i < k; i++) {
if (b == NULL) return head;
b = b->next;
}
ListNode *newNode = reverseOperator(a,b);
a->next = reverseKGroup(b,k);
return newNode;
}
ListNode* reverseOperator(ListNode* n,ListNode *b) {
ListNode *pre, *cur, *nxt;
pre = NULL; cur = n; nxt = n;
while (cur != b) {
nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
};
1、用快慢指针,找到链表尾节点接上头节点,找到旋转后的头节点,切掉前面的连接,返回该头节点即可
2、链表为空时返回,注意k有可能比链表长度还大,先遍历一遍链表求出长度并接上原头结点,k对长度求余
3、将第二个节点移动num-k-1个节点,切断连接,返回头结点即可
ListNode* rotateRight(ListNode* head, int k) {
if(head==NULL)
return NULL;
ListNode* first=head;
ListNode* second=head;
int num=1;
while(first->next!=NULL)
{
first=first->next;
num++;
}
k=k%num;
first->next=head;
for(int i=0;i<num-k-1;i++)
second=second->next;
ListNode* ans=second->next;
second->next=NULL;
return ans;
}
由于头结点可能被删掉,所以用一个dummy指向头结点,循环遍历,如果cur下两个节点相等,则删除两次
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (!head)
return head;
ListNode* dummy = new ListNode(0, head);
ListNode* cur = dummy;
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 dummy->next;
}
};
一次遍历,判断当前节点是否和下一个节点相等,相等则删除继续判断,不相等才往后挪
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (!head)
return head;
ListNode* cur = head;
while (cur->next) {
if (cur->val == cur->next->val)
cur->next = cur->next->next;
else
cur = cur->next;
}
return head;
}
};
题目要求把所有小于指定元素的值放到前面,所以可以维护两个链表,一个存小值,一个存大值,遍历一遍,最后把大值接到小值链表后面即可符号要求
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* small = new ListNode(0);
ListNode* smallHead = small;
ListNode* large = new ListNode(0);
ListNode* largeHead = large;
while (head != nullptr) {
if (head->val < x) {
small->next = head;
small = small->next;
} else {
large->next = head;
large = large->next;
}
head = head->next;
}
large->next = nullptr;
small->next = largeHead->next;
return smallHead->next;
}
};
反转指定范围的链表,首先移动头结点h到left前一个,开始反转r-l次链表,
ListNode* reverseBetween(ListNode* head, int left, int right) {
auto dummyNode = new ListNode(-1),h = dummyNode;
dummyNode->next = head;
for(int i = 0; i < left - 1; i++,h = h->next);
auto p = h->next;
for(int i = 0; i < right - left; i++){
auto q = p->next;
p->next = q->next;
q->next = h->next;
h->next = q;
}
return dummyNode->next;
}
1、用快慢指针,快指针移动两格,慢指针移动一格,如果快指针能追上慢指针,则存在环。
2、快指针起始值大于慢指针一格,方便进入循环。
3、如果有节点等于NULL,一定不存在环
4、快指针先判断有没有到表尾再移动,慢指针不用判断
bool hasCycle(ListNode *head) {
if(head==NULL||head->next==NULL) return false;
ListNode* first=head;
ListNode* second=head->next;
while(first!=second){
if(second==NULL||second->next==NULL) return false;
first=first->next;
second=second->next->next;
}
return true;
}
方法1:哈希表
一个非常直观的思路是:我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};
方法2:双指针法
初始化:快指针fast指向头结点, 慢指针slow指向头结点
让fast一次走两步, slow一次走一步,第一次相遇在C处,停止
然后让fast指向头结点,slow原地不动,让后fast,slow每次走一步,当再次相遇,就是入口结点。
NC3 链表中环的入口结点
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode *fast = pHead;
ListNode *slow = pHead;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) break;
}
if (!fast || !fast->next) return nullptr;
fast = pHead;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
们利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。
void reorderList(ListNode *head) {
vector<ListNode*> vec;
ListNode* node=head;
while(node!=NULL){
vec.push_back(node);
node=node->next;
}
int i=0,j=vec.size()-1;
while(i<j){
vec[i++]->next=vec[j];
if(i==j)
break;
vec[j]->next=vec[i];
j--;
}
vec[i]->next = NULL;
}
原地重排链表,先利用快慢指针找到链表中点,砍断两个链表,对后半段链表进行翻转,然后对两个链表进行合并
class Solution {
public:
void reorderList(ListNode* head) {
ListNode* p=head,*q=head,*r,*s=head;
if(!head) //head为空,则直接退出
return ;
while(q->next){ //寻找中间结点
q=q->next; //p走一步
p=p->next;
if(q->next)
q=q->next; //q走两步
}
q=p->next; //p所指结点为中间结点,q为后半段链表的首结点
p->next=nullptr;
while(q){ //将链表后半段逆置
r=q->next;
q->next=p->next;
p->next=q;
q=r;
}
q=p->next; //q指向后半段的第一个数据结点
p->next=nullptr;
while(q){ //将链表后半段的结点插入到指定位置
r=q->next; //r指向后半段的下一个结点
q->next=s->next; //将q所指结点插入到s所指结点(head结点)之后
s->next=q;
s=q->next; //s指向前半段的下一个插入点
q=r;
}
}
};
归并排序
中间归并之前需要把两个链表相连的地方砍断,再归并返回新链表的头结点
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == nullptr)
return head;
int length = 0;
ListNode* node = head;
while (node != nullptr) {
length++;
node = node->next;
}
ListNode* dummyHead = new ListNode(0, head);
for (int subLength = 1; subLength < length; subLength <<= 1) {
ListNode* prev = dummyHead, *curr = dummyHead->next;
while (curr != nullptr) {
ListNode* head1 = curr;
for (int i = 1; i < subLength && curr->next != nullptr; i++)
curr = curr->next;
ListNode* head2 = curr->next;
//必须砍断两个链表之间的连接
curr->next = nullptr;
curr = head2;
for (int i = 1; i < subLength && curr != nullptr && curr->next != nullptr; i++)
curr = curr->next;
ListNode* next = nullptr;
if (curr != nullptr) {
next = curr->next;
//砍断第二个链表和后面的链接
curr->next = nullptr;
}
//将砍断的链表归并排序,返回合并完的头结点
ListNode* merged = merge(head1, head2);
//将合并完的接回去,再把prev节点后移
prev->next = merged;
while (prev->next != nullptr)
prev = prev->next;
curr = next;
}
}
return dummyHead->next;
}
ListNode* merge(ListNode* head1, ListNode* head2) {
ListNode* dummyHead = new ListNode(0);
ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;
while (temp1 != nullptr && temp2 != nullptr) {
if (temp1->val <= temp2->val) {
temp->next = temp1;
temp1 = temp1->next;
} else {
temp->next = temp2;
temp2 = temp2->next;
}
temp = temp->next;
}
if (temp1 != nullptr)
temp->next = temp1;
else if (temp2 != nullptr)
temp->next = temp2;
return dummyHead->next;
}
};
递归方法
class Solution {
public:
ListNode* sortList(ListNode* head) {
return sortList(head, nullptr);
}
ListNode* sortList(ListNode* head, ListNode* tail) {
if (head == nullptr)
return head;
if (head->next == tail) {
head->next = nullptr;
return head;
}
ListNode* slow = head, *fast = head;
while (fast != tail) {
slow = slow->next;
fast = fast->next;
if (fast != tail) {
fast = fast->next;
}
}
ListNode* mid = slow;
return merge(sortList(head, mid), sortList(mid, tail));
}
ListNode* merge(ListNode* head1, ListNode* head2) {
ListNode* dummyHead = new ListNode(0);
ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;
while (temp1 != nullptr && temp2 != nullptr) {
if (temp1->val <= temp2->val) {
temp->next = temp1;
temp1 = temp1->next;
} else {
temp->next = temp2;
temp2 = temp2->next;
}
temp = temp->next;
}
if (temp1 != nullptr)
temp->next = temp1;
else if (temp2 != nullptr)
temp->next = temp2;
return dummyHead->next;
}
};
1、用双指针,每次移动一格判断是否相交,如果两个都为空返回空
2、同一链表的移动要用ifelse,先判断再移动避免重复移动
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* node1=headA;
ListNode* node2=headB;
while(node1!=node2) {
//先进行条件判断,再node1->next,否则可能对node1操作两次造成死循环
if(node1!=NULL)
node1=node1->next;
else node1=headB;
if(node2!=NULL)
node2=node2->next;
else node2=headA;
}
return node1;
}
1、用双指针法遍历链表,遍历的过程中修改链表的指向。
2、用temp指针暂存second指针指向的下一个节点。
3、最后返回的是first指针,second指针存的是NULL。
ListNode* reverseList(ListNode* head) {
//使用双指针,不用dummy开头
ListNode* first=NULL;
ListNode* second=head;
//判断结束不用next
while(second!=NULL) {
//用temp暂存下一个节点
ListNode* temp=second->next;
second->next=first;
first=second;
second=temp;
}
//first里存着的是倒转的链表,不是second
return first;
}
方法1:将值复制到数组中后用双指针法
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int> vals;
while (head != nullptr) {
vals.emplace_back(head->val);
head = head->next;
}
for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {
if (vals[i] != vals[j]) {
return false;
}
}
return true;
}
};
方法2:递归
class Solution {
ListNode* frontPointer;
public:
bool recursivelyCheck(ListNode* currentNode) {
if (currentNode != nullptr) {
if (!recursivelyCheck(currentNode->next))
return false;
if (currentNode->val != frontPointer->val)
return false;
frontPointer = frontPointer->next;
}
return true;
}
bool isPalindrome(ListNode* head) {
frontPointer = head;
return recursivelyCheck(head);
}
};
方法3:快慢指针
整个流程可以分为以下五个步骤:
找到前半部分链表的尾节点。
反转后半部分链表。
判断是否回文。
恢复链表。
返回结果。
双指针一个指奇数队列,一个指偶数队列,遍历改next指针,最后把偶接到奇数后面返回
ListNode* oddEvenList(ListNode* head) {
if(head==NULL||head->next==NULL||head->next->next==NULL)
return head;
ListNode* first=head;
ListNode* second=head->next;
ListNode* p=second;
while(second!=NULL&&second->next!=NULL){
first->next=second->next;
first=second->next;
second->next=second->next->next;
second=first->next;
}
first->next=p;
return head;
}