题目01
【 输入一个链表,按链表从尾到头的顺序返回一个ArrayList 】
方法一:使用栈来作为中间保存结构
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
stack<int> v;
vector<int> ArrayList;
for(ListNode * phead=head;phead!=NULL;phead=phead->next){
v.push(phead->val);
}
while(!v.empty()){
ArrayList.push_back(v.top());
v.pop();
}
return ArrayList;
}
};
方法二:递归调用
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> ArrayList;
vector<int> printListFromTailToHead(ListNode* head) {
if(head!=NULL){
printListFromTailToHead(head->next);
ArrayList.push_back(head->val);
}
return ArrayList;
}
};
题目02
【输入一个链表,输出该链表中倒数第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) {
int n=0,t=0;
for(ListNode *p1=pListHead;p1!=NULL;p1=p1->next){
n++;
}
for(ListNode* p=pListHead;p!=NULL;p=p->next){
t++;
if(t==n-k+1){
return p;
break;
}
}
}
};
方法二:双指针,第一个指针指向链表的开头,第二个指针相对于第一个指针向右偏移 k+1 个结点。然后两个指针同步移动,当第二个指针指向链表的结尾时,第一个指针刚好指向倒数第 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){
return NULL;
}
int n=0;
ListNode *p1=pListHead;
ListNode *p2=pListHead;
// p2指针先走k-1步,指向第k个结点
while(p2!=NULL && n<k){
n++;
p2=p2->next;
}
// 当链表的个数小于间距k时,返回空
if(p2==NULL && n<k){
return NULL;
}
while(p2!=NULL){
p1=p1->next;
p2=p2->next;
}
return p1;
}
};
题目03
【输入一个链表,反转链表后,输出新链表的表头】
分析:使用三个指针,分别指向当前的结点、前结点、后结点。然后来进行反转
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode *pPrev=NULL, *pNode=pHead, *pNext=NULL;
while(pNode!=NULL){
pNext = pNode->next; // 将当前结点的后继结点保存在pNext
pNode->next=pPrev; // 反转链表,使当前结点指向前一个结点
pPrev=pNode;
pNode=pNext;
}
return pPrev;
}
};
题目04
【输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则】
分析:通过逐个比较两个链表中的元素,按照递增的顺序来创建一个新的链表。
方法一:通过递归来进行比较。通过递归来实现会更加简洁。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1==nullptr)
return pHead2;
if(pHead2==nullptr)
return pHead1;
ListNode* pHead=nullptr;
if(pHead1->val<pHead2->val)
{
pHead=pHead1;
pHead->next=Merge(pHead1->next,pHead2);
}else
{
pHead=pHead2;
pHead->next=Merge(pHead1,pHead2->next);
}
return pHead;
}
};
方法二:通过循环进行比较
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
// 判断特殊情况,两个链表中其中之一为空
if(pHead1==NULL){
return pHead2;
}
if(pHead2==NULL){
return pHead1;
}
// 首先确定好头指针
ListNode *pHead=NULL;
ListNode *p1=pHead1, *p2=pHead2;
if(p1->val < p2->val){
pHead=p1;
p1=p1->next;
}else{
pHead=p2;
p2=p2->next;
}
// 然后再遍历待合并的两个链表
ListNode *cur=pHead;
while(p1!=NULL && p2!=NULL){
if(p1->val < p2->val){
cur->next=p1;
cur=cur->next;
p1=p1->next;
}else{
cur->next=p2;
cur=cur->next;
p2=p2->next;
}
}
// 将剩下的待合并链表部分,并到合并链表中
if(p1!=NULL){
cur->next=p1;
}
if(p2!=NULL){
cur->next=p2;
}
return pHead;
}
};
题目05
【给定单向链表的头指针和一个节点的指针,定义一个函数在O(1) 时间内删除该节点】
1、分析:
如果已知该节点不在末尾,则可以将该节点的下一个节点拷贝该节点上,覆盖该节点原来的内容,然后再将该节点指向下下个节点,将该节点的下一个节点删除。
2、代码如下:
struct ListNode
{
int mValue;
ListNode* pNext;
};
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
if (!pListHead || !pToBeDeleted)
return;
//要删除的节点不是尾节点,则可以将该节点的下一个节点赋值给该节点
//并让该节点指向下一个节点的下一个节点,再删除该节点的下一个节点即可
if (pToBeDeleted->pNext != nullptr)
{
ListNode* pTemp = pToBeDeleted->pNext;
pToBeDeleted->mValue = pTemp->mValue;
pToBeDeleted->pNext = pTemp->pNext;
delete pTemp;
pTemp = nullptr;
}
// 链表只有一个节点,即是头节点也是尾结点
else if (*pListHead == pToBeDeleted)
{
delete pToBeDeleted;
pToBeDeleted = nullptr;
*pListHead = nullptr;
}
// 链表中有多个节点时,删除尾结点
else
{
ListNode* pTemp = *pListHead;
while (pTemp->pNext!=pToBeDeleted)
{
pTemp = pTemp->pNext;
}
pTemp->pNext = nullptr;
delete pToBeDeleted;
pToBeDeleted = nullptr;
}
}
题目06
【在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5】
1、分析:可以首先创建一个节点作为链表的头节点,这样就可以不用再考虑头节点的问题
2、方法一:通过返回链表的头指针,代码如下
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
ListNode* first=new ListNode(0);
first->next=pHead;
ListNode* pPreNode=first;
ListNode* pNode=pHead;
while(pNode!=nullptr && pNode->next!=nullptr)
{
if(pNode->val==pNode->next->val)
{
int value=pNode->val;
while(pNode!=nullptr && pNode->val==value)
{
pNode=pNode->next;
}
pPreNode->next=pNode;
}
else
{
pPreNode=pNode;
pNode=pNode->next;
}
}
return first->next;
}
};
题目07
【给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null】
1、分析:将该问题分解成三个小的问题来进行解决
- 判断该链表是否存在环:用两个指针,一个指针一次走两步,一个指针一次走一步,若快的指针能追上慢的指针则存在环,否则不存在。
- 存在环之后开始计算环中节点的个数:当上面两个指针相遇时,用一个指针指向它们,然后让其一个节点一个节点的遍历,当其再次回到两个指针相遇的位置时,计数完成为n。
- 求入口节点的指针:一个指针指向头节点,另一个指针先走n-1个步,当两个指针相遇时,即为入口节点的位置
2、代码如下:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* meetNode=MeetNode(pHead);
if(meetNode==nullptr)
return nullptr;
int count=1;
ListNode* pCount=meetNode;
while(pCount->next!=meetNode)
{
++count;//求链表中环形节点的个数
pCount=pCount->next;
}
//求入口节点
ListNode* pAhead=pHead;
ListNode* pBehind=pHead;
for(unsigned int i=0;i<count;++i)
{
pAhead=pAhead->next;
}
while(pAhead!=pBehind)
{
pAhead=pAhead->next;
pBehind=pBehind->next;
}
return pAhead;
}
//查找链表的入口节点
ListNode* MeetNode(ListNode* pHead)
{
if(pHead==nullptr)
return nullptr;
ListNode* pSlow=pHead->next;
if(pSlow==nullptr)
return nullptr;
ListNode* pFast=pSlow->next;
while(pSlow!=nullptr && pFast!=nullptr)
{
if(pFast==pSlow)
return pFast;
pSlow=pSlow->next;
pFast=pFast->next;
if(pFast!=nullptr)
pFast=pFast->next;
}
return nullptr;
}
};
题目08
【输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head】
1、分析:
(1)、可以将每个节点先进行复制,并组合成一个长的链表。此时只考虑每个节点的下一个节点,不考虑随机节点。如下图
(2)、再复制随机链接节点
(3)、再将该长链表进行拆分,奇数位置上的节点连接起来即为要求的新链表。
2、代码
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNext(pHead);
CloneRandom(pHead);
return CreateCloneList(pHead);
}
// 只添加每个节点的复制节点
void CloneNext(RandomListNode* pHead)
{
RandomListNode* p=pHead;
while(p!=nullptr)
{
RandomListNode* pTemp=new RandomListNode(0);
pTemp->label=p->label;
pTemp->next=p->next;
pTemp->random=nullptr;
p->next=pTemp;
p=pTemp->next;
}
}
// 复制每个节点的随机链接节点
void CloneRandom(RandomListNode* pHead)
{
RandomListNode *p=pHead;
while(p!=nullptr)
{
if(p->random!=nullptr)
{
p->next->random=p->random->next;
}else
{
p->next->random=nullptr;
}
p=p->next->next;
}
}
// 将长链表进行拆分出满足需要的新链表。
RandomListNode* CreateCloneList(RandomListNode *pHead)
{
RandomListNode *pNewHead=nullptr,*pNewNode=nullptr,*pNode=pHead;
if(pNode!=nullptr)
{
pNewHead=pNewNode=pNode->next;
pNode->next=pNewNode->next;
pNode=pNode->next;
}
while(pNode!=nullptr)
{
pNewNode->next=pNode->next;
pNewNode=pNewNode->next;
pNode->next=pNewNode->next;
pNode=pNode->next;
}
return pNewHead;
}
};
题目09
【输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向】
1、分析:
使用中序遍历来进行排序
2、代码
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
TreeNode *pLast=nullptr;
ConvertNode(pRootOfTree,&pLast);
TreeNode *pHead=pLast;
while(pHead!=nullptr && pHead->left!=nullptr)
pHead=pHead->left;
return pHead;
}
void ConvertNode(TreeNode *pNode,TreeNode** pLastNode)
{
if(pNode==nullptr)
return;
TreeNode *pCur=pNode;
if(pCur->left!=nullptr)
{
// 递归的结果是求 *pLastNode 的值,用于接下来的计算
// *pLastNode 为指向已排序链表最后一个节点的指针
ConvertNode(pCur->left,pLastNode);
}
pCur->left=*pLastNode;
if(*pLastNode!=nullptr)
(*pLastNode)->right=pCur;
*pLastNode=pCur;
if(pCur->right!=nullptr)
{
ConvertNode(pCur->right,pLastNode);
}
}
};