算法
排序
插入排序
思想:通过构建有序数列,将新的元素,从右往左寻找,找到正确的位置进行插入。
步骤:1.从第二个元素开始,将新元素插入到前面已经排序好的子数组中;
2.新元素插入之后,新位置到原来位置之间的元素,全部向右平移一位;
3.重复前两个步骤,直至所有的元素排序完成。
public class InsertSort {
/**
* 思路:从左到右,每次把新元素正确插入到左边已经排序号的数组中
*/
public void insert(int[] nums){
int length = nums.length;
for (int i=1;i<length;i++){
int baseNum = nums[i];
int j = i-1;
while (j>=0 && nums[j]>baseNum){
nums[j+1]=nums[j];
j--;
}
nums[j+1]=baseNum;
}
}
}
复杂度:O(n^2)
优化思路:从右往左插入的时候,因为左边的数组已经排序好了,则可以利用二分查找法进行。
public void binaryInsert(int[] nums){
int length = nums.length;
for (int i=1;i<length;i++){
int baseNum = nums[i];
int left = 0;
int right = i;
while (left<right){
int mid = (left+right)/2;
if (nums[mid]<baseNum){
left=mid+1;
}else {
right=mid;
}
}
for (int j=i;j>left;j--){
nums[j]=nums[j-1];
}
nums[left]=baseNum;
}
}
复杂度:O(nlogn)
快排
思路:分治算法
选择最左边的数字为基础数字,找到其在数组中的正确位置,然后将左边和右边的数组重复刚才的操作
1.根据基准数字找到其正确的位置:从右边开始循环,数字大于等于基准数字,则位置不变,右边索引-1继续找
当右边数字小于基准数字时,将右边索引的值,赋值给左索引的值,此时右边索引的值不一定是正确的
2.从左边开始循环,数字小于等于基准数字,则位置不变,左边索引+1继续找
当左边数字大于基准数字时,将左边索引的值,赋值给右索引的值,此时左边索引的值不一定是正确的
3. 1,2不断循环,直至左索引和右索引相等时停止,这时左索引的值不一定是正确的,需要将基准数字赋值给它
4.返回此时的左索引值,即为基准数字的正确索引位置
public void quick(int[] num,int left,int right){
if (left>=right){
return;
}
Integer partition = partition(num, left, right);
quick(num,left,partition-1);
quick(num,partition+1,right);
}
public Integer partition(int[] num,int left,int right){
int baseNum = num[left];
while (left<right){
while (left<right && baseNum<=num[right]){
right--;
}
//此时right位置的数字不一定是正确的
num[left]=num[right];
while (left<right && baseNum>=num[left]){
left++;
}
//此时left处的数字不一定是正确的
num[right]=num[left];
}
//此时left处的数字不一定是正确的,要把基准数字赋值给它才是正确的
num[left]=baseNum;
return left;
}
复杂度:
平均复杂度:n logn
最坏复杂度:n^2
单调栈
下一个更大元素
题目:nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
示例 2:
输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。
提示:
1 <= nums1.length <= nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 104
nums1和nums2中所有整数 互不相同
nums1 中的所有整数同样出现在 nums2 中
方法一:暴力求解
时间复杂度:O(mn),其中 m 是 nums1的长度,n 是 nums2的长度。
方法二:单调递减栈+哈希表
时间复杂度:O(m+n),其中 m 是 nums1的长度,n 是 nums2的长度。我们需要遍历 nums2以计算 nums2中每个元素右边的第一个更大的值;需要遍历 nums1以生成查询结果。
思路:遍历num2中的元素,构建一个单调递减栈,遍历每个元素时,只要把当前元素和栈顶元素存到hashMap中,便利num1,将num1中的元素key获取value即可。
public int[] test(int[] arr,int[] part){
Stack<Integer> stack = new Stack<>();
HashMap<Integer,Integer> num2BigNum = new HashMap<>();
int[] ans = new int[part.length];
for (int i=arr.length-1;i>=0;i--){
if (stack.size()==0 || stack.peek()>arr[i]){
}else {
while (stack.size()>0 && stack.peek()<=arr[i]){
stack.pop();
}
}
if (stack.size()==0){
num2BigNum.put(arr[i],-1);
}else {
num2BigNum.put(arr[i],stack.peek());
}
stack.push(arr[i]);
}
for (int i=0;i<part.length;i++){
ans[i] = num2BigNum.get(part[i]);
}
return ans;
}