首页 > 其他 > 详细

重刷剑指offer

时间:2019-09-13 13:21:04      阅读:77      评论:0      收藏:0      [点我收藏+]

数组中重复的数字

题目描述

在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重
复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。

思路:

数组大小为n,数字范围为0~n-1,也就是说如果数组不重复,则排序后每个数组i上的元素就应该是i

为此,我们可以模拟这个排序的过程,如果i位置上的元素不为i,则把它与nums[i]位置的元素进行交换,这相当于把nums[i]放到他该去的地方。如果nums[i]位置的元素已经是i了,则这刚好就是重复的数字;否则将交换到i位置上的数字继续这个过程,如果交换过来的数等于i,则说明这个数放在了他应该放的地方,继续处理i+1上的数,如果仍然不等,则将这个换来的数放到他应该放的地方....最后,对于i位置来说,要么找到了值为i的元素,要么找到了重复的元素,是不可能出现无限循环的,因为无限循环要求换来的数字与i位置的数字相等,但这时候已经判定重复并跳出返回了。

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    bool duplicate(int numbers[], int length, int* duplication) {
        if(length<=1)
			return false;
		int p1=0;
		while(p1<length)
		{
			if(numbers[p1]!=p1)
			{
				while(numbers[p1]!=p1)
				{
					int right=numbers[numbers[p1]];
					if(numbers[p1]==right)
					{
						*duplication=right;
						return true;
					}
					swap(numbers[numbers[p1]],numbers[p1]);
				}
			}
			p1++;
		}
		return false;
    }
	void swap(int& a,int& b)
	{
		int tmp=a;
		a=b;
		b=tmp;
	}
};

  

二维数组中的查找

题目描述

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:
一开始想用两次二分,这实际上是不可行的
比如:
7
[1,2,8,9]
[2,4,9,12]
[4,7,10,13]
[6,8,11,15]
实际上7并不是出现在第四行
问题出现在不能保证比数组头元素大的元素一定出现在这一行,不能保证上一行的某个元素一定会大于这一行的头元素,因为只能保证头元素大于上一行的头元素。对上一行的头元素来说相当于有两个递增方向。只有每行的头都大于上一行的尾,也就是严格单调递增时,可以这样写 
 
实际上只要找到只有一个方向递增,一个方向递减的方向,从这个位置开始搜索,即相当于遍历一个有序数组。这个位置在左下角或右上角。左下角向右递增,向上递减;右上角向左递减,向下递增。
class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        auto& nums=array;
		if(nums.empty()||nums[0].empty())
			return false;
		int row=nums.size()-1;
		int col=0;
		while(row>=0&&col<nums[row].size())
		{
			if(nums[row][col]==target)
				return true;
			else if(nums[row][col]<target)
				col++;
			else
				row--;
		}
		return false;
    }
};

  

替换空格

题目描述

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路:
最优解当然是不适用空间复杂度
首先计算拓展后的空间大小,然后用2个指针指向2个尾,逐个进行复制。
唯一值得注意的是,由于传进来的是str,因此尾部要取‘\0‘的位置,因为也要把‘\0‘也拷进去。
因此2个尾部分别是:

int pLast=len+count*2;
int pCur=len;

而不是

int pLast=len+count*2-1;
int pCur=len-1;

虽然这一种也能实现,但少了个‘\0‘,需要自己在补一个。

class Solution {
public:
	void replaceSpace(char *str,int length) {
		int len=length;
		int count=0;
		for(int i=0;i<len;i++)
		{
			if(str[i]==‘ ‘)
				count++;
		}
		int pLast=len+count*2;
		int pCur=len;
		while(pCur>=0&&pLast>pCur)
		{
			if(str[pCur]!=‘ ‘)
			{
				str[pLast]=str[pCur];
				pLast--;
			}
			else
			{
				str[pLast--]=‘0‘;
				str[pLast--]=‘2‘;
				str[pLast--]=‘%‘;
			}
			pCur--;
		}
		return ;
	}
};

  

从尾到头打印链表

题目描述

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
思路:
最简单的就是push进vector后reverse,如果不用这种方式,递归也可以
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> ans;
		if(!head)
			return ans;
		Core(ans,head);
		return ans;
    }
	void Core(vector<int>& in,ListNode* head)
	{
		if(!head)
			return;
		Core(in,head->next);
		in.push_back(head->val);
	}
};

  

重建二叉树★★★

思路很清晰的题,但容易出错,多写几次注意下细节,特别是传参长度的处理。

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
要唯一的确定一个二叉树,必须知道两种遍历方式,且其中必须要有一个中序遍历,因为前序与后序只能分割出父与子的关系,而无法分割出左右子树的关系。
 
思路:
根据前序的第一个数,找到中序中的那个数的位置,然后将其分割为左右子树,相当于说前序提供父子关系,中序提供左右子树关系。
然后递归这个过程。
值得注意的是Core中的传参,其实只要前序的起点,中序的起点与长度就可以了,因为前序与中序的长度必须一致。如果每次传入前序的起点与终点,中序的起点与终点,则太复杂,也太麻烦。
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
		if(pre.empty()||pre.size()!=vin.size())
			return NULL;
		return reConstructCore(pre,vin,0,0,pre.size());
    }
	TreeNode* reConstructCore(vector<int>& pre,vector<int>& vin,int pstart,int vstart,int len)
	{
		if(len<=0)
			return NULL;
		int tmp=pre[pstart];
		int pos=0;
		for(;pos<len&&vin[pos+vstart]!=tmp;pos++);
		pos+=vstart;
		int left=pos-vstart;
		int right=len-left-1;
		TreeNode* root=new TreeNode(tmp);
		root->left=reConstructCore(pre,vin,pstart+1,vstart,left);
		root->right=reConstructCore(pre,vin,pstart+left+1,pos+1,right);
		return root;
	}
};

  

二叉树的下一个结点★★★

要考虑的情况很多,值得好好看看,体味下二叉树的结构

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路:
 其实就是分为多种情况:
1).当前节点是父亲节点的左子树时:
此时要遍历该节点的右子树:
如果右子树存在,则找到右子树根节点的最左边的一个节点
如果右子树不存在,以此点为根的树已经遍历完了,此时由于它是父节点的左子树,因此下一个应该遍历父节点
2).当前节点时父亲节点的右子树时:
此时要遍历该节点的右子树:
如果右子树存在,则找到右子树根节点的最左边的一个节点
如果右子树不存在,则不仅以此节点为根的的子树已经遍历完了,由于是父节点的右子树,相当于父节点也遍历完了;如果父节点是祖父节点的右子树,则相当于祖父节点也已经遍历完了。。。。。
因此要找到祖先节点中的某一个节点,该节点要么父亲节点时空,要么父亲节点的左子树的根就是这个节点。
当为选中的祖先节点的父节点为空,相当于已经遍历完了所有节点,返回NULL
如果非空,则相当于已经遍历完了该祖先节点父节点的左子树,此时应该返回该祖先的父亲节点。
 
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
		if(!pNode)
			return NULL;
		return GetNextCore(pNode);
    }
	TreeLinkNode* GetNextCore(TreeLinkNode* node)
	{
		if(!node->next)
		{
			auto it=node->right;
			if(!it)
				return NULL;
			while(it->left)
				it=it->left;
			return it;
		}
		auto pLast=node->next;
		if(pLast->left==node)
		{
			if(node->right)
			{
				auto right=node->right;
				while(right->left)
					right=right->left;
				return right;
			}
			else
				return pLast;
		}
		else if(pLast->right==node)
		{
			if(node->right)
			{
				auto right=node->right;
				while(right->left)
					right=right->left;
				return right;
			}
			else
			{
				while(pLast->next&&pLast->next->right==pLast)
					pLast=pLast->next;
				if(pLast->next==NULL)
					return NULL;
				else
					return pLast->next;
			}
		}
		else
			return NULL;
	}
};

  这个分类讨论太多了,其实完全可以简化:

1).如果该节点存在右子树,则应该遍历右子树中的最左节点

2).如果不存在,则要找到某个节点,该节点是其父的左子节点(包括目前的这个节点)

这样就简化很多了

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
		if(!pNode)
			return NULL;
		auto right=pNode->right;
		if(right)
		{
			while(right->left)
				right=right->left;
			return right;
		}
		else
		{
			while(pNode->next&&pNode->next->right==pNode)
				pNode=pNode->next;
			if(pNode->next)
				return pNode->next;
			else
				return NULL;
		}
    }
};

用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

思路:

栈的特点是先入后出,队列的特点是先入先出

现在给了两个栈,其实可以想象成这样一个情景:

Tail和Head相当于两个水管,水从tail中流入,从Head中流出,这样实际上就相当于实现了队列

我们在Tail中灌水,就相当于push进数,然后pop提出最先灌进去的数,则从Head中读出。

 

 技术分享图片

 

 如果Head现在是空,也就是说没有元素,则把Tail里的数全部转到Head中,当然顺序要变化,这也就相当于上图里用个管子把两个容器相连,然后把Tail底部的水抽到Head中

每次push都push到Tail中。

class Solution
{
public:
    void push(int node) {
		stack2.push(node);
    }
    int pop() {
        if(!stack1.empty())
		{
			auto ans=stack1.top();
			stack1.pop();
			return ans;
		}
		else
		{
			if(stack2.empty())
				return INT_MIN;
			while(!stack2.empty())
			{
				stack1.push(stack2.top());
				stack2.pop();
			}
			auto ans=stack1.top();
			stack1.pop();
			return ans;
		}
    }
private:
    stack<int> stack1;//head
    stack<int> stack2;//tail
};

  

用两个队列实现栈

重刷剑指offer

原文:https://www.cnblogs.com/lxy-xf/p/11516404.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!