数组类型笔试题2
1. 搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1
你可以假设数组中不存在重复的元素
示例1
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int end = nums.size()-1;
while (left<end){
int mid = (left + end)/2;
if (nums[mid]==target){
return mid;
}
if (nums[mid] > nums[end]){
if (nums[left]<=target && target<=nums[mid]){
end = mid;
}
else{
left = mid+1;
}
}
else{
if (nums[mid] <= target && target<= nums[end]){
left = mid+1;
}
else{
end = mid-1;
}
}
}
return nums[left]==target ?left:-1;
}
};
2. 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
思路:其实这就是一个排序过程,可以使用冒泡,快速,选择,插入,归并…但是用排序算法有点大材小用了,因为这个数据是比较简单的,只有三个数字,我们定义三个指针就可以了。
思路:定义三个指针,left = 0,mid = 0,right = len(num)-1
始终用 num[mid]与0,1,2这三个数字进行判断,如果等于0,则与第一个指针交换,如果等于2则与最后一个指针交换,如果等于1不做交换,指针继续往前走,当mid>=right 的时候,退出循环。
class Solution {
public:
void sortColors(vector<int>& nums) {
int left = 0, mid = 0;
int right = nums.size()-1;
while(mid<=right){
if(nums[mid]==2){
swap(nums[mid],nums[right]);
right-=1;
}
else if(nums[mid]==0){
swap(nums[mid],nums[left])
left+=1;
mid+=1;
}
else{
mid+=1;
}
}
}
};
3. 删除排序数组中的重复项 II
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例1
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。
示例2
给定 nums = [0,0,1,1,1,1,2,3,3],
函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。
思路:定义两个指针,只要保证第 i +2 个数不等于 i 位置的数就可以了
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int index = 0;
for(int i = 0;i<nums.size();++i){
if(index<2 || nums[i] > nums[index-2]){
nums[index] = nums[i];
index += 1;
}
}
return index;
}
};
4. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例
输入: [2,1,5,6,2,3]
输出: 10
思路:单调栈
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
heights.push_back(0);
stack<int> s;
int maxArea = 0;
for(int i = 0;i<heights.size();i++){
while(!s.empty() && heights[i]<heights[s.top()]){
int top= s.top();
s.pop();
// 计算出宽度
int weith = s.empty()?i:(i-s.top()-1);
maxArea = max(maxArea,heights[top]*weith);
}
s.push(i);
}
return maxArea;
}
};
5. 买股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例1
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例2
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
暴力法简单,但是时间太长
在遍历的过程我们要做两件事,第一件事就是记录到目前为止最小的值,第二件事是要 记录当前值减去最小值的大小。并且更新这两个值…
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0
minprice = prices[0]
max_price = 0
for i in range(1,len(prices)):
if prices[i]<minprice:
minprice = prices[i]
if prices[i]-minprice>max_price:
max_price = prices[i]-minprice
return max_price
6. 买卖股票的最佳时机 II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
示例1
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例2
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例3
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
思路:看起来好像很复杂的样子,其实都是花架子
就比如实例2:[1,2,3,4,5] 第一天买进来,第五天买出去,完全可以认为第一天买,第二天卖了,第二天再买,第三天再卖了,第三天再买回来,第四天再卖了,第四天再买一次,到第五天卖了,这样一来也就相当于第一天买,第五天卖,他们的利润是一样的。
所以我们在遍历的时候,只要发现后一项比前一项大,就跌加起来。
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0
total = 0
for i in range(1,len(prices)):
if prices[i]>prices[i-1]:
total +=prices[i]-prices[i-1]
return total
7. 用一个数组实现三个栈
三个栈互相是没有关系的,如果每个栈满了,则不进行插入,如果没有元素,弹出的时候会返回-1
class TripleInOne {
private:
vector<int> s;
int stackSize;
int spointer[3];
public:
TripleInOne(int stackSize) {
s = vector<int>(stackSize*3, 0);
this->stackSize = stackSize;
spointer[0] = 0;
spointer[1] = stackSize;
spointer[2] = stackSize*2;
}
//然后将元素push进去的话我们首先看有没有溢出的
void push(int stackNum, int value) {
if(spointer[stackNum] < (stackNum+1)*stackSize){
s[spointer[stackNum]++] = value;
}
}
//这里的pop的话就看是否有元素让你pop出来,没有的话就返回-1
int pop(int stackNum) {
int res = -1;
if(spointer[stackNum] > (stackNum)*stackSize){
res = s[--spointer[stackNum]];
}
return res;
}
// peek的操作和上面的pop操作类似,
// 不同的点就是我们不需要把指针往后退一步
int peek(int stackNum) {
int res = -1;
if(spointer[stackNum] > (stackNum)*stackSize){
res = s[spointer[stackNum]-1];
}
return res;
}
//看看是不是空的话我们就看每个栈的指针是不是在原来的初始位置上
bool isEmpty(int stackNum) {
return spointer[stackNum] == stackNum*stackSize;
}
};
8. 堆盘子
堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子
请实现数据结构SetOfStacks,由多个栈组成,并且在前一个栈填满时新建一个栈。此外,SetOfStacks.push()和SetOfStacks.pop()应该与普通栈的操作方法相同(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。 进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作
当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,pop,popAt 应返回 -1.
class StackOfPlates {
public:
StackOfPlates(int cap) {
this->capacity = cap;
}
void push(int val) {
if(capacity <=0){
return;
}
//需要扩容啦
if(list.empty() || list.back().size()==capacity){
list.push_back(stack<int>());
}
list.back().push(val);
}
int pop() {
if(capacity <=0 || list.empty()){
return -1;
}
int res = list.back().top();
list.back().pop();
if(list.back().empty()){
list.pop_back();
}
return res;
}
int popAt(int index) {
// 越界了
if(capacity <=0 || index >=list.size()){
return -1;
}
// 为什么要用迭代器呢?因为是随机删除,里面有库可以使用
auto iter = list.begin();
while(index--){
++iter;
}
int res = (*iter).top();
(*iter).pop();
if((*iter).empty()){
iter = list.erase(iter);
}
return res;
}
private:
list<stack<int>>list;
int capacity;
};