【91 算法-基础篇】01.数组,栈,队列
例题一:66. 加一
class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;
for(int i = n-1; i > -1; i--){
if(digits[i] == 9)
digits[i] = 0;
else{
digits[i] += 1;
return digits;
}
}
digits = new int[n+1];
digits[0] = 1;
return digits;
}
}
例题二:75.颜色分类
思路
采用三个指针,一个指针curr从0开始遍历数组,一个指针p0从0开始指向待交换成0的位置,一个指针p2从nums.length-1开始指向待交换成2的位置。当curr遇到0时,和p0位置的元素交换,并且p0和curr都向后移动一位;当curr遇到2时,和p2位置的元素交换,但此时只向前移动p2,curr不动,这是因为从p2交换来的元素可能是0、1、2任意一个数,而从p0交换来的元素只可能是1;当curr遇到1时,继续向后移动。
代码
class Solution {
public void sortColors(int[] nums) {
int p0 = 0, p2 = nums.length - 1, curr = 0;
while(curr <= p2){
if(nums[curr] == 0){
swap(curr, p0, nums);
p0++;
curr++;
}
else if(nums[curr] == 2){
swap(curr, p2, nums);
p2--;
}
else
curr++;
}
}
private void swap(int i, int j, int[] nums){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
例题三:1381. 设计一个支持增量操作的栈
请你设计一个支持下述操作的栈。
实现自定义栈类 CustomStack :
CustomStack(int maxSize):用 maxSize 初始化对象,maxSize 是栈中最多能容纳的元素数量,栈在增长到 maxSize 之后则不支持 push 操作。
void push(int x):如果栈还未增长到 maxSize ,就将 x 添加到栈顶。
int pop():弹出栈顶元素,并返回栈顶的值,或栈为空时返回 -1 。
void inc(int k, int val):栈底的 k 个元素的值都增加 val 。如果栈中元素总数小于 k ,则栈中的所有元素都增加 val 。
示例:
输入:
[“CustomStack”,“push”,“push”,“pop”,“push”,“push”,“push”,“increment”,“increment”,“pop”,“pop”,“pop”,“pop”]
[[3],[1],[2],[],[2],[3],[4],[5,100],[2,100],[],[],[],[]]
输出:
[null,null,null,2,null,null,null,null,null,103,202,201,-1]
解释:
CustomStack customStack = new CustomStack(3); // 栈是空的 []
customStack.push(1); // 栈变为 [1]
customStack.push(2); // 栈变为 [1, 2]
customStack.pop(); // 返回 2 --> 返回栈顶值 2,栈变为 [1]
customStack.push(2); // 栈变为 [1, 2]
customStack.push(3); // 栈变为 [1, 2, 3]
customStack.push(4); // 栈仍然是 [1, 2, 3],不能添加其他元素使栈大小变为 4
customStack.increment(5, 100); // 栈变为 [101, 102, 103]
customStack.increment(2, 100); // 栈变为 [201, 202, 103]
customStack.pop(); // 返回 103 --> 返回栈顶值 103,栈变为 [201, 202]
customStack.pop(); // 返回 202 --> 返回栈顶值 202,栈变为 [201]
customStack.pop(); // 返回 201 --> 返回栈顶值 201,栈变为 []
customStack.pop(); // 返回 -1 --> 栈为空,返回 -1
提示:
1 <= maxSize <= 1000
1 <= x <= 1000
1 <= k <= 1000
0 <= val <= 100
每种方法 increment,push 以及 pop 分别最多调用 1000 次
思路
主要说一下increment()函数,最直接的一种方法是直接对栈中满足要求的元素重新赋值,这种方法的时间复杂度为 O ( m i n ( k , t o p ) ) O(min(k, top)) O(min(k,top))。为了将时间复杂度降为 O ( 1 ) O(1) O(1),我们可以用空间换时间,具体做法是维护一个数组increments[],长度为maxSize,将增量赋值到increments[min(k-1, top)]处,当pop()时将arr[top]+increments[top]输出。
代码
class CustomStack {
private int[] arr;
private int maxSize;
private int top;
private int[] increments;
public CustomStack(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
increments = new int[maxSize];
top = -1;
}
public void push(int x) {
if(!isFull()){
arr[++top] = x;
}
}
public int pop() {
if(!isEmpty()){
int val = arr[top] + increments[top];
top--;
if(!isEmpty())
increments[top] += increments[top+1];
increments[top+1] = 0;
return val;
}
else
return -1;
}
public void increment(int k, int val) {
if(k > 0 && !isEmpty()){
int index = k-1 < top ? k-1 : top;
increments[index] += val;
}
}
public boolean isEmpty(){
return top == -1;
}
public boolean isFull(){
return top == maxSize - 1;
}
}
复杂度
时间复杂度:全部都是
O
(
1
)
O(1)
O(1)
空间复杂度:我们维护了一个大小为 maxSize 的数组,因此平均到每次的空间复杂度为
O
(
m
a
x
S
i
z
e
/
N
)
O(maxSize / N)
O(maxSize/N),其中 N 为操作数。
我们还可以用一个动态数组实现increments[],这样空间复杂度就可以降低到
O
(
m
a
x
S
i
z
e
/
t
o
p
)
O(maxSize / top)
O(maxSize/top),top为栈的当前长度
例题四:394. 字符串解码
思路
运用了栈,将字符依次入栈,遇到’]'则弹出括号之间的内容,并根据‘[’之前的数字进行处理,处理后的字符串入栈,直至原字符串全部入栈。
代码
class Solution {
public String decodeString(String s) {
Deque<String> stack = new ArrayDeque<>();
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(c == ']'){
String str = "";
while(!stack.peek().equals("[")){
str = stack.pop() + str;
}
stack.pop();
String numString = "";
while(stack.peek() != null && Character.isDigit(stack.peek().charAt(0))){
numString = stack.pop() + numString;
}
int num = Integer.parseInt(numString);
String temp = str;
for(int j = 0; j < num - 1; j++){
str += temp;
}
stack.push(str);
}else{
stack.push(String.valueOf(c));
}
}
String res = "";
while(!stack.isEmpty()){
res = stack.pop() + res;
}
return res;
}
}
例题五:232. 用栈实现队列
思路
用两个栈来实现队列,push的数据放入stack1中,pop或peek时先看stack2,若stack2为空,则将stack1中的数据依次放入stack2中,再从stack2中pop或peek。
代码
class MyQueue {
Deque<Integer> stack1;
Deque<Integer> stack2;
/** Initialize your data structure here. */
public MyQueue() {
stack1 = new ArrayDeque<Integer>();
stack2 = new ArrayDeque<Integer>();
}
/** Push element x to the back of queue. */
public void push(int x) {
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(!stack2.isEmpty()){
return stack2.pop();
}else if(!stack1.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.pop();
}
return -1;
}
/** Get the front element. */
public int peek() {
if(!stack2.isEmpty()){
return stack2.peek();
}else if(!stack1.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.peek();
}
return -1;
}
/** Returns whether the queue is empty. */
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}
复杂度
时间复杂度为
O
(
n
)
O(n)
O(n),但对于多次操作来说,因为不是每一次pop或peek都要将stack1中的数据放入stack2,所以平均下来也可以看做
O
(
1
)
O(1)
O(1)。
空间复杂度为
O
(
n
)
O(n)
O(n)。n为stack1和stack2的size之和。
例题六:380. 常数时间插入、删除和获取随机元素
思路
采用数组和哈希表想结合的方法,哈希表存储数组中的数和它对应的数组下标。
- 插入:首先要查询待插入的数是否存在,这时用哈希表来查,时间复杂度为 O ( 1 ) O(1) O(1),若不存在,直接将数插入到数组的末尾和哈希表中,时间复杂度都为 O ( 1 ) O(1) O(1)
- 删除:首先要查询待删除的数是否存在,同上。若存在,通过哈希表找到它的数组下标,再讲数组中的这个数和数组最后一个数交换,然后删除数组中最后一个数最后再把哈希表中的数删除,这两种删除操作时间复杂度都为 O ( 1 ) O(1) O(1)
- 随机获取:通过数组下标的方式获取元素,时间复杂度为 O ( 1 ) O(1) O(1)
代码
class RandomizedSet {
/** Initialize your data structure here. */
Map<Integer, Integer> map;
ArrayList<Integer> arr;
Random random;
public RandomizedSet() {
map = new HashMap<Integer, Integer>();
arr = new ArrayList<Integer>();
random = new Random();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(map.containsKey(val)){
return false;
}
else{
map.put(val, arr.size());
arr.add(val);
return true;
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(map.containsKey(val)){
int index = map.get(val);
int temp = arr.get(arr.size()-1);
arr.set(index, temp);
map.put(temp, index);
map.remove(val);
arr.remove(arr.size()-1);
return true;
}else{
return false;
}
}
/** Get a random element from the set. */
public int getRandom() {
return arr.get(random.nextInt(arr.size()));
}
}
复杂度
- 时间复杂度为 O ( 1 ) O(1) O(1)
- 空间复杂度为 O ( N ) O(N) O(N),N为存储的元素个数