剑指offer 链表系列题目

1: 从尾到头打印链表

题目:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

解析:既然是从尾到头打印链表,那么这种先遍历最后出来的,不正符合栈的结构么?所以使用栈保存这些节点的值即可。

代码:

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) 
    {
        vector<int>vec;
        stack<int>st;
        ListNode* p = head;
        while(p)
        {
            int val = p->val;
            st.push(val);
            p = p->next;
        }
        while(!st.empty())
        {
            int top = st.top();
            vec.push_back(top);
            st.pop();
        }
        return vec;  
    }
};

2:链表中倒数第k个结点

题目:输入一个链表,输出该链表中倒数第k个结点。

解析:求倒数第k个节点,那么怎么一次遍历搞定呢?可以让平p1指针先走k-1步,然后p1,p2在同步而行,当p1到达尾部时,p2所指的节点就是倒数第k个节点。
在循环的时候,一定注意k,k如果比整个链表的长度还大,那么可能k还没为0,而p早已到了链表的末尾的下一个节点。 我在这里错了好久,没发现。

代码:

/*
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; 
        ListNode* p1 = pListHead;
        ListNode* p2 = pListHead;
        unsigned int forward_k = k-1;
        while(forward_k)
        {
            p1 = p1->next;
            --forward_k;
            if(p1==NULL) return NULL;
        }
        if(!(p1->next))  return p2;
        while(p1)
        {
            p1 = p1->next;
            p2 = p2->next;
            if(p1->next==NULL) break;
        }
        return p2;
    }
};

3:反转链表

题目:
输入一个链表,反转链表后,输出新链表的表头。

解析:
最开始我觉得可能题目给的链表里应该带头结点,但是后来试了下,没有带头结点,但是一般处理链表的时候都会带头结点,所以我认为的生成一个头结点。反转链表常用的方法是头插法,只要搞清楚指针的指向就行,注意

cur = p; //在这之后,立马应该改变p的值,不然cur头插以后,指针丢掉了。

代码:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution 
{
public:
    ListNode* ReverseList(ListNode* pHead) 
    {
        if(pHead==NULL) return NULL;
        ListNode* ppHead = (ListNode*)malloc(sizeof(ListNode));
        ppHead->val = 0;
        ppHead->next = NULL;
        ListNode* p = pHead;
        if(!p)  return pHead;
        ListNode* cur;
        while(p)
        {
            cur = p;
            p = p->next;
            cur->next = ppHead->next;
            ppHead->next = cur;
            if(!p)  break;
        }
        return ppHead->next;
    }
};

4:合并两个排序的链表

题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

解析:可以申明一个头结点,然后依次比较两个链表的值,采用尾插法,最后如果p1位空了,那么直接把p2剩余的链表连接在后面即可。反之p2空了也是一样。

非递归版本
代码:

/*
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 *p1,*p2;
        p1 = pHead1;
        p2 = pHead2;
        ListNode *pHead = new ListNode(1);
        pHead->next = NULL;
        ListNode *p = pHead;
        while(p1&&p2)
        {
            ListNode* temp;
            int val1 = p1->val;
            int val2 = p2->val;
            if(val1<=val2)
            {
                temp = p1;
                p1 = p1->next;
            }
            else 
            {
                temp = p2;
                p2 = p2->next;
            }
            temp->next = NULL;
            p->next = temp;
            p = temp;
            if(p1==NULL||p2==NULL) break;
        }
        if(!p1)  p->next  = p2;
        else p->next = p1;
        return pHead->next;
    }
};

递归版本

/*
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* pNewHead = NULL;
        if(pHead1->val<=pHead2->val)
        {
            pNewHead = pHead1;
            pNewHead->next = Merge(pHead1->next,pHead2);
        }
        else 
        {
            pNewHead = pHead2;
            pNewHead->next = Merge(pHead1,pHead2->next);
        }
        return pNewHead;
    }
};

5:复杂链表的复制

题目描述:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

这道题有两种解法。1:使用哈希表。2:在原链表中对每个节点都复制一次。

###1:哈希表的方法

首先我们另外建立另外一条相同的链表,暂时将random置为0,然后在建立这个新链表的时候,使用哈希表将对应的节点存起来,map<N,N*>。然后新建链表以后就可以对新节点的rangdom进行设置了,怎么设置呢?

newNode->random = mp[oldNode->random]

###2:复制节点,然后将新旧链分开

在原来链表的基础上,通知个一模一样的节点,然后在这个新链表的基础上,设置random,然后将新旧链表分开。

新旧链表分开具体实现为:

	RandomListNode* pNode = pHead;
	RandomListNode* pCloneHead = pNode->next;
	RandomListNode* pCloneNode = pCloneHead;
	
	pNode->next = pCloneNode->next;
	pNode = pCloneNode->next;

	while (pNode != NULL)
	{
		pCloneNode->next = pNode->next;
		pCloneNode = pNode->next;
		pNode->next = pCloneNode->next;
		pNode = pCloneNode->next;
	}

完整的调试代码和具体算法代码如下,Clone为哈希的方法,Clone1为复制节点的做法

#include "stdafx.h"
#include<iostream>
#include<vector>
#include<stack>
#include<map>
using namespace std;

struct RandomListNode {
	int label;
	struct RandomListNode *next, *random;
	RandomListNode(int x) :
		label(x), next(NULL), random(NULL) {
	}
};


RandomListNode* Clone(RandomListNode* pHead)
{
	if (pHead == NULL) return NULL;
	map<RandomListNode*, RandomListNode*>mpNode;
	RandomListNode *cur, *newCur;
	RandomListNode* Head = pHead;  //新链表的头结点
	RandomListNode* newHead = new RandomListNode(Head->label);
	mpNode[pHead] = newHead;  //将新旧链表的节点对应起来  
	RandomListNode* p = pHead; //p是对应旧链表的遍历节点
	RandomListNode* pNew = newHead;//pNew是对应的新链表的遍历节点
	//顺序遍历的时候存储另外一边的节点
	while (p->next)
	{
		cur = p->next;
		newCur = new RandomListNode(cur->label);
		pNew->next = newCur;
		mpNode[cur] = newCur;
		pNew = newCur;
		p = p->next;
	}
	

	pNew = newHead;
	cout << pNew->label<<" ";
	///遍历一下
	while (pNew->next)
	{
		cout << pNew->next->label << " ";
		pNew = pNew->next;
	}

	cout << endl << "---------------------------------------" << endl;
	
	p = pHead;
	pNew = newHead;
	pNew->random = mpNode[p->random];
	while (p->next)
	{
		cur = p->next;
		newCur = pNew->next;
		newCur->random = mpNode[cur->random];
		p = p->next;
		pNew = pNew->next;
	}
	pNew = newHead;
	cout << pNew->label << " " << pNew->random->label << " " << endl;;
	///遍历一下
	while (pNew->next)
	{
		cout << pNew->next->label << " ";
		if (pNew->next->random != NULL)  cout << pNew->next->random->label << " " << endl;
		else cout << endl;
		pNew = pNew->next;
	}
	return newHead;
}


RandomListNode* Clone1(RandomListNode* pHead)
{
	if (pHead == NULL)  return pHead;
	RandomListNode* pCur = pHead;
	while (pCur != NULL)
	{
		int val = pCur->label;
		RandomListNode* pNode = new RandomListNode(val);
		pNode->next = pCur->next;
		pCur->next = pNode;
		pCur = pCur->next->next;
	}
	pCur = pHead;
	while (pCur != NULL)
	{
		RandomListNode* pCurNext = pCur->next;
		if (pCur->random) pCurNext->random = pCur->random->next;
		pCur = pCur->next->next;
	}

	RandomListNode* pNode = pHead;
	RandomListNode* pCloneHead = pNode->next;
	RandomListNode* pCloneNode = pCloneHead;
	
	pNode->next = pCloneNode->next;
	pNode = pCloneNode->next;

	while (pNode != NULL)
	{
		pCloneNode->next = pNode->next;
		pCloneNode = pNode->next;
		pNode->next = pCloneNode->next;
		pNode = pCloneNode->next;
	}

	RandomListNode* p = pCloneHead;
	
	while (p != NULL)
	{
		cout << p->label << " ";
		if(p->random) cout<<p->random->label << endl;
		else cout << endl;
		p = p->next;
	}

	cout << "------------------------------------" << endl;

	p = pHead;

	while (p != NULL)
	{
		cout << p->label << " ";
		if (p->random) cout << p->random->label << endl;
		else cout << endl;
		p = p->next;
	}
	return pCloneHead;
}


int main()
{
	RandomListNode* p1 = new RandomListNode(3);
	RandomListNode* p2 = new RandomListNode(5);
	RandomListNode* p3 = new RandomListNode(8);
	RandomListNode* p4 = new RandomListNode(9);
	RandomListNode* p5 = new RandomListNode(100);
	p1->next = p2;
	p2->next = p3;
	p3->next = p4;
	p4->next = p5;
	p5->random = p1;
	p1->random = p2;
	p4->random = p3;
	//Clone(p1);
	Clone1(p1);
	return 0;
}

6 两个链表的第一个公共结点

题目:
输入两个链表,找出它们的第一个公共结点。

思路:在这里插入图片描述

如果两个链表有公共节点,那么只有可能是这样情形,所以只要算出两个链表的差值,然后长的那个先移动差值,然后在慢慢比较,就能找到第一个公共的节点了。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) 
    {
        if(pHead1==NULL||pHead2==NULL)  return NULL;
        int len_1,len_2;
        len_1 = len_2 = 0;
       	ListNode* pHead = pHead1;
       	while(pHead)
       	{
       		len_1++;
       		pHead = pHead->next;
       	}
       	pHead = pHead2;

       	while(pHead)
       	{
       		len_2++;
       		pHead = pHead->next;
       	}
       	int cnt = (len_1>len_2)?len_1-len_2:len_2-len_1;
       	bool ok = len_1>len_2;
       	if(ok)
       	{  
       		while(cnt)  
       		{
       			pHead1 = pHead1->next;
       			--cnt;
       		}
       	}
       	else
       	{
       		while(cnt)  
       		{
       			pHead2 = pHead2->next;
       			--cnt;
       		}
       	} 
       		
       	while(pHead1&&pHead2&&pHead1!=pHead2)
       	{
       		pHead1 = pHead1->next;
       		pHead2 = pHead2->next;
       	}
       	return pHead1;
        
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值