1.链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead==NULL||k==0){
return NULL;
}
ListNode *pAhead=pListHead;
ListNode *pBehind=NULL;
for(unsigned int i=0;i<k-1;++i){
if(pAhead->next!=NULL){
pAhead=pAhead->next;
}
else{
return NULL;
}
}
pBehind=pListHead;
while(pAhead->next!=NULL){
pAhead=pAhead->next;
pBehind=pBehind->next;
}
return pBehind;
}
};
2.反转链表
输入一个链表,反转链表后,输出链表的所有元素。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pReversedHead=NULL;
ListNode* pNode=pHead;
ListNode* pPrev=NULL;
while(pNode!=NULL){
ListNode* pNext=pNode->next;
if(pNext==NULL)
pReversedHead=pNode;
pNode->next=pPrev;
pPrev=pNode;
pNode=pNext;
}
return pReversedHead;
}
};
3.复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
方法二:next指针关联
创建新链表的时候,用原结点的next指针指向对应新结点,新结点的next指针指向下一个原结点,以此类推,形成之字形关联。然后,就可以先更新新链表的random指针,再解除next关联,更新next指针。这种方法不需要map来辅助,不管查找next还是random指针都是O(1)的,效率很高。
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL) return NULL;
RandomListNode *newHead = new RandomListNode(pHead->label);
RandomListNode *pHead1=NULL, *pHead2=NULL;
// 上链,使新旧链表成之字形链接
for(pHead1=pHead,pHead2=newHead;pHead1;){
RandomListNode* tmp = pHead1->next;
pHead1->next = pHead2;
pHead2->next = tmp;
// next
pHead1 = tmp;
if(tmp) pHead2 = new RandomListNode(tmp->label);
else pHead2 = NULL;
}
// 更新新链表的random指针
for(pHead1=pHead,pHead2=newHead;pHead1;){
if(pHead1->random) pHead2->random = pHead1->random->next;
else pHead2->random = NULL;
pHead1 = pHead2->next;
if(pHead1) pHead2 = pHead1->next;
else pHead2 = NULL;
}
// 脱链,更新各链表的next指针
for(pHead1=pHead,pHead2=newHead;pHead1;){
pHead1->next = pHead2->next;
if(pHead1->next) pHead2->next = pHead1->next->next;
else pHead2->next = NULL;
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return newHead;
}
};
方法一:map关联
首先遍历一遍原链表,创建新链表(赋值label和next),用map关联对应结点;再遍历一遍,更新新链表的random指针。(注意map中应有NULL ----> NULL的映射)
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL) return NULL;
map<RandomListNode*,RandomListNode*> m;
RandomListNode* pHead1 = pHead;
RandomListNode* pHead2 = new RandomListNode(pHead1->label);
RandomListNode* newHead = pHead2;
m[pHead1] = pHead2;
while(pHead1){
if(pHead1->next) pHead2->next = new RandomListNode(pHead1->next->label);
else pHead2->next = NULL;
pHead1 = pHead1->next;
pHead2 = pHead2->next;
m[pHead1] = pHead2;
}
pHead1 = pHead;
pHead2 = newHead;
while(pHead1){
pHead2->random = m[pHead1->random];
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return newHead;
}
};
4.两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
unsigned int nlength1=GetListLength(pHead1);
unsigned int nlength2=GetListLength(pHead2);
int nlengthDif=nlength1-nlength2;
ListNode* pListHeadLong=pHead1;
ListNode* pListHeadShort=pHead2;
if(nlength2>nlength1){
pListHeadLong=pHead2;
pListHeadShort=pHead1;
nlengthDif=nlength2-nlength1;
}
for(int i=0;i<nlengthDif;++i){
pListHeadLong=pListHeadLong->next;
}
while((pListHeadLong!=NULL)&&(pListHeadShort!=NULL)&&(pListHeadLong!=pListHeadShort)){
pListHeadLong=pListHeadLong->next;
pListHeadShort=pListHeadShort->next;
}
ListNode* pFirstCommonNode=pListHeadLong;
return pFirstCommonNode;
}
unsigned int GetListLength(ListNode* pHead){
unsigned int nlength=0;
ListNode* pNode=pHead;
while(pNode!=NULL){
++nlength;
pNode=pNode->next;
}
return nlength;
}
};
5.链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
/*
时间复杂度为O(n),两个指针,一个在前面,另一个紧邻着这个指针,在后面。
两个指针同时向前移动,每移动一次,前面的指针的next指向NULL。
也就是说:访问过的节点都断开,最后到达的那个节点一定是尾节点的下一个,
也就是循环的第一个。
这时候已经是第二次访问循环的第一节点了,第一次访问的时候我们已经让它指向了NULL,
所以到这结束。
*/
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (!pHead->next)
return NULL;
ListNode* previous = pHead;
ListNode* front = pHead ->next;
while (front)
{
previous->next = NULL;
previous = front;
front = front->next;
}
return previous;
}
};
6.删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if (pHead==NULL)
return NULL;
if (pHead!=NULL && pHead->next==NULL)
return pHead;
ListNode* current;
if ( pHead->next->val==pHead->val){
current=pHead->next->next;
while (current != NULL && current->val==pHead->val)
current=current->next;
return deleteDuplication(current);
}
else {
current=pHead->next;
pHead->next=deleteDuplication(current);
return pHead;
}
}
};
7.访问单个节点的删除
实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。
给定带删除的节点,请执行删除操作,若该节点为尾节点,返回false,否则返回true
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Remove {
public:
bool removeNode(ListNode* pNode) {
if(pNode == NULL || (pNode != NULL && pNode->next == NULL )) return false;
pNode->val = pNode->next->val;
pNode->next = pNode->next->next;
delete pNode->next;
return true;
}
};
8.链表分割
编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
给定一个链表的头指针 ListNode* pHead,请返回重新排列后的链表的头指针。注意:分割以后保持原来的数据顺序不变。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
ListNode* beforeStart=NULL;
ListNode* beforeEnd=NULL;
ListNode* afterstart=NULL,*afterEnd=NULL;
ListNode* headval;
while(pHead)
{
headval=pHead->next;
if(pHead->val<x)
{
if(beforeEnd==NULL)
beforeEnd=beforeStart=pHead;
else
{
beforeEnd->next=pHead;
beforeEnd=pHead;
}
}
else
{
if(afterstart==NULL)
{
afterstart=afterEnd=pHead;
}
else
{
afterEnd->next=pHead;
afterEnd=pHead;
}
}
pHead=headval;
}
if(afterEnd)
afterEnd->next=NULL;
if(beforeEnd!=NULL)
beforeEnd->next=afterstart;
else
beforeStart=afterstart;
return beforeStart;
}
};
9.链式A+B
有两个用链表表示的整数,每个结点包含一个数位。这些数位是反向存放的,也就是个位排在链表的首部。编写函数对这两个整数求和,并用链表形式返回结果。
给定两个链表ListNode* A,ListNode* B,请返回A+B的结果(ListNode*)。
测试样例:
{1,2,3},{3,2,1}
返回:{4,4,4}
本题的思路很简单,按照小学数学中学习的加法原理从末尾到首位,对每一位对齐相加即可。技巧在于如何处理不同长度的数字,以及进位和最高位的判断。这里对于不同长度的数字,我们通过将较短的数字补0来保证每一位都能相加。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Plus {
public:
ListNode* plusAB(ListNode* a, ListNode* b) {
// 头结点
ListNode *head = new ListNode(-1);
ListNode *p = head;
ListNode *node = nullptr;
int c = 0,sum,val1,val2;
ListNode *pa = a,*pb = b;
//加法
while(pa != nullptr || pb != nullptr || c != 0){
val1 = (pa == nullptr ? 0 : pa->val);
val2 = (pb == nullptr ? 0 : pb->val);
sum = val1 + val2 + c;
// 进位
c = sum / 10;
node = new ListNode(sum % 10);
//尾插法
p->next = node;
p = node;
pa = (pa == nullptr ? nullptr : pa->next);
pb = (pb == nullptr ? nullptr : pb->next);
}//while
return head->next;
}
};
10.回文链表
请编写一个函数,检查链表是否为回文。
给定一个链表ListNode* pHead,请返回一个bool,代表链表是否为回文。
测试样例:
{1,2,3,2,1}
返回:true
{1,2,3,2,3}
思路:利用快慢指针,找到中间节点;将慢指针节点的值压入栈,到达中间节点后,依次出栈与后续节点的值比较。特别注意长度奇偶数。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Palindrome {
public:
bool isPalindrome(ListNode* pHead) {
// write code here
if(pHead == NULL)
return true;
stack<int> ss;
ListNode* p = pHead;
ListNode* q = pHead;
ss.push(p->val);
while(q->next != NULL && q->next->next != NULL)
{
p = p->next;
ss.push(p->val);
q = q->next->next;
}
if(q->next == NULL) //长度为奇数
ss.pop();
p = p->next;
while(!ss.empty())
{
if(ss.top() != p->val)
break;
p = p->next;
ss.pop();
}
if(ss.empty())
return true;
else
return false;
}
};
输入一个链表,输出该链表中倒数第k个结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead==NULL||k==0){
return NULL;
}
ListNode *pAhead=pListHead;
ListNode *pBehind=NULL;
for(unsigned int i=0;i<k-1;++i){
if(pAhead->next!=NULL){
pAhead=pAhead->next;
}
else{
return NULL;
}
}
pBehind=pListHead;
while(pAhead->next!=NULL){
pAhead=pAhead->next;
pBehind=pBehind->next;
}
return pBehind;
}
};
2.反转链表
输入一个链表,反转链表后,输出链表的所有元素。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pReversedHead=NULL;
ListNode* pNode=pHead;
ListNode* pPrev=NULL;
while(pNode!=NULL){
ListNode* pNext=pNode->next;
if(pNext==NULL)
pReversedHead=pNode;
pNode->next=pPrev;
pPrev=pNode;
pNode=pNext;
}
return pReversedHead;
}
};
3.复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
方法二:next指针关联
创建新链表的时候,用原结点的next指针指向对应新结点,新结点的next指针指向下一个原结点,以此类推,形成之字形关联。然后,就可以先更新新链表的random指针,再解除next关联,更新next指针。这种方法不需要map来辅助,不管查找next还是random指针都是O(1)的,效率很高。
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL) return NULL;
RandomListNode *newHead = new RandomListNode(pHead->label);
RandomListNode *pHead1=NULL, *pHead2=NULL;
// 上链,使新旧链表成之字形链接
for(pHead1=pHead,pHead2=newHead;pHead1;){
RandomListNode* tmp = pHead1->next;
pHead1->next = pHead2;
pHead2->next = tmp;
// next
pHead1 = tmp;
if(tmp) pHead2 = new RandomListNode(tmp->label);
else pHead2 = NULL;
}
// 更新新链表的random指针
for(pHead1=pHead,pHead2=newHead;pHead1;){
if(pHead1->random) pHead2->random = pHead1->random->next;
else pHead2->random = NULL;
pHead1 = pHead2->next;
if(pHead1) pHead2 = pHead1->next;
else pHead2 = NULL;
}
// 脱链,更新各链表的next指针
for(pHead1=pHead,pHead2=newHead;pHead1;){
pHead1->next = pHead2->next;
if(pHead1->next) pHead2->next = pHead1->next->next;
else pHead2->next = NULL;
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return newHead;
}
};
方法一:map关联
首先遍历一遍原链表,创建新链表(赋值label和next),用map关联对应结点;再遍历一遍,更新新链表的random指针。(注意map中应有NULL ----> NULL的映射)
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL) return NULL;
map<RandomListNode*,RandomListNode*> m;
RandomListNode* pHead1 = pHead;
RandomListNode* pHead2 = new RandomListNode(pHead1->label);
RandomListNode* newHead = pHead2;
m[pHead1] = pHead2;
while(pHead1){
if(pHead1->next) pHead2->next = new RandomListNode(pHead1->next->label);
else pHead2->next = NULL;
pHead1 = pHead1->next;
pHead2 = pHead2->next;
m[pHead1] = pHead2;
}
pHead1 = pHead;
pHead2 = newHead;
while(pHead1){
pHead2->random = m[pHead1->random];
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return newHead;
}
};
4.两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
unsigned int nlength1=GetListLength(pHead1);
unsigned int nlength2=GetListLength(pHead2);
int nlengthDif=nlength1-nlength2;
ListNode* pListHeadLong=pHead1;
ListNode* pListHeadShort=pHead2;
if(nlength2>nlength1){
pListHeadLong=pHead2;
pListHeadShort=pHead1;
nlengthDif=nlength2-nlength1;
}
for(int i=0;i<nlengthDif;++i){
pListHeadLong=pListHeadLong->next;
}
while((pListHeadLong!=NULL)&&(pListHeadShort!=NULL)&&(pListHeadLong!=pListHeadShort)){
pListHeadLong=pListHeadLong->next;
pListHeadShort=pListHeadShort->next;
}
ListNode* pFirstCommonNode=pListHeadLong;
return pFirstCommonNode;
}
unsigned int GetListLength(ListNode* pHead){
unsigned int nlength=0;
ListNode* pNode=pHead;
while(pNode!=NULL){
++nlength;
pNode=pNode->next;
}
return nlength;
}
};
5.链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
/*
时间复杂度为O(n),两个指针,一个在前面,另一个紧邻着这个指针,在后面。
两个指针同时向前移动,每移动一次,前面的指针的next指向NULL。
也就是说:访问过的节点都断开,最后到达的那个节点一定是尾节点的下一个,
也就是循环的第一个。
这时候已经是第二次访问循环的第一节点了,第一次访问的时候我们已经让它指向了NULL,
所以到这结束。
*/
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (!pHead->next)
return NULL;
ListNode* previous = pHead;
ListNode* front = pHead ->next;
while (front)
{
previous->next = NULL;
previous = front;
front = front->next;
}
return previous;
}
};
6.删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if (pHead==NULL)
return NULL;
if (pHead!=NULL && pHead->next==NULL)
return pHead;
ListNode* current;
if ( pHead->next->val==pHead->val){
current=pHead->next->next;
while (current != NULL && current->val==pHead->val)
current=current->next;
return deleteDuplication(current);
}
else {
current=pHead->next;
pHead->next=deleteDuplication(current);
return pHead;
}
}
};
7.访问单个节点的删除
实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。
给定带删除的节点,请执行删除操作,若该节点为尾节点,返回false,否则返回true
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Remove {
public:
bool removeNode(ListNode* pNode) {
if(pNode == NULL || (pNode != NULL && pNode->next == NULL )) return false;
pNode->val = pNode->next->val;
pNode->next = pNode->next->next;
delete pNode->next;
return true;
}
};
8.链表分割
编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
给定一个链表的头指针 ListNode* pHead,请返回重新排列后的链表的头指针。注意:分割以后保持原来的数据顺序不变。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
ListNode* beforeStart=NULL;
ListNode* beforeEnd=NULL;
ListNode* afterstart=NULL,*afterEnd=NULL;
ListNode* headval;
while(pHead)
{
headval=pHead->next;
if(pHead->val<x)
{
if(beforeEnd==NULL)
beforeEnd=beforeStart=pHead;
else
{
beforeEnd->next=pHead;
beforeEnd=pHead;
}
}
else
{
if(afterstart==NULL)
{
afterstart=afterEnd=pHead;
}
else
{
afterEnd->next=pHead;
afterEnd=pHead;
}
}
pHead=headval;
}
if(afterEnd)
afterEnd->next=NULL;
if(beforeEnd!=NULL)
beforeEnd->next=afterstart;
else
beforeStart=afterstart;
return beforeStart;
}
};
9.链式A+B
有两个用链表表示的整数,每个结点包含一个数位。这些数位是反向存放的,也就是个位排在链表的首部。编写函数对这两个整数求和,并用链表形式返回结果。
给定两个链表ListNode* A,ListNode* B,请返回A+B的结果(ListNode*)。
测试样例:
{1,2,3},{3,2,1}
返回:{4,4,4}
本题的思路很简单,按照小学数学中学习的加法原理从末尾到首位,对每一位对齐相加即可。技巧在于如何处理不同长度的数字,以及进位和最高位的判断。这里对于不同长度的数字,我们通过将较短的数字补0来保证每一位都能相加。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Plus {
public:
ListNode* plusAB(ListNode* a, ListNode* b) {
// 头结点
ListNode *head = new ListNode(-1);
ListNode *p = head;
ListNode *node = nullptr;
int c = 0,sum,val1,val2;
ListNode *pa = a,*pb = b;
//加法
while(pa != nullptr || pb != nullptr || c != 0){
val1 = (pa == nullptr ? 0 : pa->val);
val2 = (pb == nullptr ? 0 : pb->val);
sum = val1 + val2 + c;
// 进位
c = sum / 10;
node = new ListNode(sum % 10);
//尾插法
p->next = node;
p = node;
pa = (pa == nullptr ? nullptr : pa->next);
pb = (pb == nullptr ? nullptr : pb->next);
}//while
return head->next;
}
};
10.回文链表
请编写一个函数,检查链表是否为回文。
给定一个链表ListNode* pHead,请返回一个bool,代表链表是否为回文。
测试样例:
{1,2,3,2,1}
返回:true
{1,2,3,2,3}
思路:利用快慢指针,找到中间节点;将慢指针节点的值压入栈,到达中间节点后,依次出栈与后续节点的值比较。特别注意长度奇偶数。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Palindrome {
public:
bool isPalindrome(ListNode* pHead) {
// write code here
if(pHead == NULL)
return true;
stack<int> ss;
ListNode* p = pHead;
ListNode* q = pHead;
ss.push(p->val);
while(q->next != NULL && q->next->next != NULL)
{
p = p->next;
ss.push(p->val);
q = q->next->next;
}
if(q->next == NULL) //长度为奇数
ss.pop();
p = p->next;
while(!ss.empty())
{
if(ss.top() != p->val)
break;
p = p->next;
ss.pop();
}
if(ss.empty())
return true;
else
return false;
}
};