面试题第二弹
题一
一个栈依次压入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;
}
与上面的归并代码比一比是不是很类似。