面试题第二弹

经典面试题第二弹

题一

一个栈依次压入1、2、3、4、5,那么从栈顶到栈底分别是5、4、3、2、1。将这个栈逆置后,从栈顶到栈底依次是1、2、3、4、5,也就是实现栈内元素的逆序,但只能用递归函数来实现,不能使用其他数据结构。


思路:
理解题目意思是说实现栈的逆置只能用递归方法来完成,且你所能用到的数据结构只能是栈。
首先我们考虑这样一个函数,它能返回并删除一个栈的栈底元素(当然也只能用递归方法实现),栈所拥有的删除元素的方法仅有一个即pop()函数,该函数是将栈顶元素返回并移除,它和我们想实现的函数的功能相似都是返回并移除一个元素,只不过移除的元素的位置不同。所以,我觉得我们可以从这儿下手,如何下手呢?pop()函数是移除栈顶元素,而我们想移除栈底元素,什么时候这两个函数想等呢?答案呼之欲出,即当栈中只有一个元素的时候两者功能完全一样,所以我们可以使用这样的结构

fun(stack)
int result = stack.pop()
stack.isEmpty()?
True: return result
False:int last = fun(stack)
stack.push(result)
reutrn last


上述结构中有一个先弹栈后压栈的操作,是不是很熟悉的?(在回溯算法中这个结构用到的挺多的)这个操作保证我们只是pop()了栈底元素,而其它元素依旧保存相对位置不变放在栈中.
OK,这个函数想出来后事情已经解决了一大半,之后我们就要用这个函数来解决题目的问题,怎么解决呢?我们这个函数可以获取一个栈的栈底元素,再结合递归函数,我们每一次递归都获取到当前栈的栈底元素,当到达最后一次递归时(严格来说应该是倒数第二次)就将获取的栈底元素push()进去,回归时一层一层的push(),最后栈就被逆置了。


上代码:

template<typename T>
T getAndRemoveLastElement(stack<T>& st)
{
	T result = st.top();
	st.pop();
	if (st.empty())
	{
		return result;
	}
	else
	{
		T lastElement = getAndRemoveLastElement(st);
		st.push(result);
		return lastElement;
	}
}

template<typename T>
void reverse(stack<T>&st) 
{
	if (st.empty())
	{
		return;
	}
	T lastElement = getAndRemoveLastElement(st);
	reverse(st);
	st.push(lastElement);
}

题二


数组小和的定义如下:
例如,数组s=[1,3,5,2, 4, 6],在s[0]的左边小于或等于s[0]的数的和为0,在s[1] 的左边小于或等于s[1]的数的和为1,在s[2]的左边小于或等于s[2]的数的和为1+3=4,在s[3]的左边小于或等于s[3]的数的和为1,在s[4]的左边小于或等于s[4]的数的和为1+3+2=6,在s[5]的左边小于或等于s[5]的数的和为1+3+5+2+4=15,所以s的小和为0+1+4+1+6+15-27。

给定一个数组s, 实现函数返回s的小和。


开门见山的说,这题要使用Merge Sort的思想,如果你使用遍历的方法时间复杂度为O(n^2),太慢了,用我们这个方法可以降到O(nlog n)的级别。
But,在此之前,我们先回忆一下Merge Sort的具体内容和代码实现。


Merge Sort

归并排序实质是分治算法,有递归版本的和迭代版本的,经典版本的归并排序使用的是递归方法,所以我们此处就是用递归来做。
Merge Sort首先是划分,注意此处的划分是逻辑上的划分,而不是物理上的划分,划分完成后对左右两个数组使用两个指针进行排序,将排序结果先放入一个临时数组,代排序完后再放回数组。
上代码:

void _Merge_Sort(int arr[],int left,int right,int temp[])
{
	if (left >= right) 
		return;

	int mid = left + ((right - left) >> 1);
	//递归
	_Merge_Sort(arr, left, mid, temp);
	_Merge_Sort(arr, mid + 1, right, temp);

	//排序
	int begin1 = left, end1 = mid;//左部分
	int begin2 = mid+1, end2 = right;//右部分

	int index = left;//辅助数组
	while (begin1<=end1&&begin2<=end2)//当任意一个数组排序完后就结束,之后来处理剩余部分
	{
		if (arr[begin1]<=arr[begin2])
		{
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1<=end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2<=end2)
	{
		temp[index++] = arr[begin2++];
	}

	index = left;//再将辅助数组复制到原数组
	while (index<=right)
	{
		arr[index] = temp[index];
		++index;
	}
}

void Merge_Sort(int arr[], int len) 
{
	int* temp = new int[len];
	_Merge_Sort(arr, 0, len - 1, temp);
	delete[] temp;
}

接下来就是精彩的部分了。我开头说了本题可以使用归并排序的思想来做,Why? and How?

Why?

How

上代码:

int _Merge_Sort(int arr[],int left,int right,int temp[])
{
	if (left >= right) 
		return 0	;

	int mid = left + ((right - left) >> 1);
	//递归
	//important
	int a = _Merge_Sort(arr, left, mid, temp);
	int b = _Merge_Sort(arr, mid + 1, right, temp);
	int smallSum = 0;
	//排序
	int begin1 = left, end1 = mid;//左部分
	int begin2 = mid+1, end2 = right;//右部分

	int index = left;//辅助数组
	while (begin1<=end1&&begin2<=end2)//当任意一个数组排序完后就结束,之后来处理剩余部分
	{
		if (arr[begin1]<=arr[begin2])
		{
			//important
			smallSum += arr[begin1] * (right-begin2+1);
			temp[index++] = arr[begin1++];
		}
		else
		{
			temp[index++] = arr[begin2++];
		}
	}
	while (begin1<=end1)
	{
		temp[index++] = arr[begin1++];
	}
	while (begin2<=end2)
	{
		temp[index++] = arr[begin2++];
	}

	index = left;//再将辅助数组复制到原数组
	while (index<=right)
	{
		arr[index] = temp[index];
		++index;
	}
	//important
	return a + b + smallSum;
}

int Merge_Sort(int arr[], int len) 
{
	if (len==0)
	{
		return 0;

	}
	int* temp = new int[len];
	int a = _Merge_Sort(arr, 0, len - 1, temp);
	delete[] temp;
	return a;
}

与上面的归并代码比一比是不是很类似。

posted @ 2019-07-26 18:53  Mr.Second  阅读(228)  评论(0编辑  收藏  举报