链表操作(04)

题目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);
        }
        
    }
};

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值