环形数组
如何实现环形数组
思路:
- 使用一个putIndex和一个getIndex表示插入的位置和获取的位置,但是这样就要考虑两个指针的追赶情况,如果都是0是代表初始化还是代表已经追赶上了?
- 增加一个size变量 插入和获取只需要判断size大小即可
public class RingArray {
private int[] arr;
private int pushi;// end
private int polli;// begin
private int size;
private int limit;
public RingArray(int limit) {
arr = new int[limit];
pushi = 0;
polli = 0;
size = 0;
this.limit = limit;
}
public void push(int value) {
if (size == limit) {
throw new RuntimeException("队列满了,不能再加了");
}
size++;
arr[pushi] = value;
pushi = nextIndex(pushi);
}
public int pop() {
if (size == 0) {
throw new RuntimeException("队列空了,不能再拿了");
}
size--;
int ans = arr[polli];
polli = nextIndex(polli);
return ans;
}
public boolean isEmpty() {
return size == 0;
}
// 如果现在的下标是i,返回下一个位置
private int nextIndex(int i) {
return i < limit - 1 ? i + 1 : 0;
}
}
如何实现线程安全的? 提示:使用AtomicInteger原子类
最小栈
实现一个特殊的栈,在基本功能的基础上,再实现返回栈中最小元素的功能
- pop、 push、 getMin操作的时间复杂度都是 0(1)。
- 设计的栈类型可以使用现成的栈结构。
思路:使用两个栈,第二个栈记录每次的最小值
public class GetStackMin {
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public GetStackMin() {
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
public void push(int newNum) {
if (this.stackMin.isEmpty()) {
this.stackMin.push(newNum);
} else if (newNum < this.getmin()) {
this.stackMin.push(newNum);
} else {
int newMin = this.stackMin.peek();
this.stackMin.push(newMin);
}
this.stackData.push(newNum);
}
public int pop() {
if (this.stackData.isEmpty()) {
throw new RuntimeException("Your stack is empty.");
}
this.stackMin.pop();
return this.stackData.pop();
}
public int getmin() {
if (this.stackMin.isEmpty()) {
throw new RuntimeException("Your stack is empty.");
}
return this.stackMin.peek();
}
}
Master公式
Master公式
形如
T(N)=aT(N/b) + O(N^d)(其中的a、b、d都是常数)的递归函数,可以直接通过Master公式来确定时间复杂度。aT(N/b) 是递归方法的时间复杂度
O(N^d)是递归方法之前的代码的时间复杂度
如果 log(b,a) < d,复杂度为O(N^d)
如果 log(b,a) >d,复杂度为O(N^log(b,a))
如果 log(b,a) == d,复杂度为O(N^d * logN)
案例:分析下面代码中获取数组最大值的时间复杂度
// 求arr中的最大值
public static int getMax(int[] arr) {
return process(arr, 0, arr.length - 1);
}
// arr[L..R]范围上求最大值 L ... R N
public static int process(int[] arr, int L, int R) {
// arr[L..R]范围上只有一个数,直接返回,base case
if (L == R) {
return arr[L];
}
// L...R 不只一个数
// mid = (L + R) / 2
int mid = L + ((R - L) >> 1); // 中点 1
int leftMax = process(arr, L, mid);
int rightMax = process(arr, mid + 1, R);
return Math.max(leftMax, rightMax);
}
这个递归方法在递归左右两个的最大值 左边规模N/2 右边N/2 调用两次,求mid值 和 Math.max()是常数项,最终 T(N)=2*T(N/2) + O(N^0) 符合上面的公式。a = 2, b= 2, d = 0; log(2,2) > 1 则时间复杂度 = O(N^log(2,2)) = O(N)
如果修改代码为下面的
public static int process(int[] arr, int L, int R) {
// arr[L..R]范围上只有一个数,直接返回,base case
if (L == R) {
return arr[L];
}
for (int i = 0; i < R; i++) {
for (int j = i; j < R; j++) {
System.out.println("");
}
}
// L...R 不只一个数
// mid = (L + R) / 2
int mid = L + ((R - L) >> 1); // 中点 1
int mid2 = mid/2;
int leftMax = process(arr, L, mid);
int leftMax2 = process(arr, mid, mid2);
int rightMax = process(arr, mid2, R);
return Math.max(leftMax, rightMax);
}
分析公式为 T(N)=3*T(N/3) + O(N^2) 最终复杂度 log(3,3) = 1 < 2
= O(N^2)