目录
排序算法总结
相关概念
稳定:如果a原本在b前面,而a=b时,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b时,排序之后a可能出现在b的后面。
内排序:所有排序操作都在内存中完成。
外排序:通常是由于数据太大,不能同时存放在内存中,根据排序过程的需要而在外存与内存之间 数据传输才能进行。
时间复杂度:时间频度,一个算法执行所耗费的时间。算法中通常用数据比较次数与数据移动次数 进行衡量。
空间复杂度:算法执行所需要的内存大小。
冒泡排序
实现逻辑
- 使用两层for循环排序,外层的for每循环一次就完成了对一个数的排序
- 每完成一次,下一次需要比较的次数就少一次
- 内循环时两两循环比较,若当前数字较大,就放到右边
- 动图如下
性能分析
- 外层循环 n 次,内层最多时循环 n – 1次、最少循环 0 次,平均循环(n-1)/2,所以循环体内总的比较交换次数为:n*(n-1) / 2 = (n^2-n)/2 ,按照计算时间复杂度的规则,去掉常数、去掉最高项系数,时间复杂度为O(N^2)
-
最优的空间复杂度为开始元素已排序,则空间复杂度为 0,最差的空间复杂度为开始元素为逆排序,则空间复杂度为 O(N),平均的空间复杂度为O(1)
-
冒泡排序是稳定排序
关键代码
public void bubbleSort(int[] nums) {
// i 代表已排序的数字个数 当 i = nums.length 说明排序结束
for (int i = 0; i < nums.length; i++) {
//当还有 nums.length - i 个数需要排序时,
//nums[0] 需要和另外 nums.length - i - 1个数比较
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
插入排序
实现逻辑
(想想斗地主排牌的顺序-插空)
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置后
- 重复步骤2345
性能分析
如果插入排序的目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况:
(1) 最好情况:序列已经是升序排列,在这种情况下,需要进行的比较操作需(n-1)次即可。
(2) 最坏情况:序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。
插入排序的赋值操作是比较操作的次数减去(n-1)次。平均来说插入排序算法复杂度为O(N^2)。
最优的空间复杂度为开始元素已排序,则空间复杂度为 0;
最差的空间复杂度为开始元素为逆排序,则空间复杂度最坏时为 O(N);
平均的空间复杂度为O(1)
关键代码
public void insertSort(int[] nums){
// 从第一个元素开始,该元素可以认为已经被排序
for (int i = 1; i < nums.length; i++) {
int temp = nums[i];
int j;
//在排第i个数的时候 是在前面 i-i个已排序的数字里找位置
for (j = i - 1; j >= 0; j--) {
if (nums[j] > temp) nums[j + 1] =nums[j];
else break;
}
//这时 nums[j] < temp 就把 temp 给 nums[j + 1]
nums[j + 1] = temp;
}
}
插入排序
实现逻辑
性能分析
关键代码
插入排序
实现逻辑
性能分析
关键代码
插入排序
实现逻辑
性能分析
关键代码
插入排序
实现逻辑
性能分析
关键代码
L0977 有序数组的平方
思路
双指针倒序放置
终止条件为区间内无元素
public int[] sortedSquares(int[] nums) {
int[] resArr = new int[nums.length];
int left = 0, right = nums.length - 1, index = nums.length - 1;
while (left <= right) {
int leftSquare = nums[left] * nums[left];
int rightSquare = nums[right] * nums[right];
if (leftSquare >= rightSquare) {
resArr[index--] = leftSquare;
left++;
} else {
resArr[index--] = rightSquare;
right--;
}
}
return resArr;
}
L0209 长度最小的子数组
滑动窗口找连续子集自我总结
- 对于每一个right 也就是右窗边 可能存在多个合适的左窗边(left)
- 所以对于每一个right 用for循环去遍历
- 每一个right有多个left 这一步用while循环去遍历
public int minSubArrayLen(int target, int[] nums) {
int left = 0, sum = 0, res = nums.length + 1;
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
res = Math.min(res, right - left + 1);
sum -= nums[left++];
}
}
return res == nums.length + 1 ? 0 : res;
}
L0059 螺旋矩阵
模拟思路
这样写的话对于单双数不同的情况需要分别套路,因为当left == rigth时,已经不会进入下面的四个循环,会导致中间的值为0(默认值)。
或者是把下面四个循环的都变成大于等于、小于等于,这样的话相当于是每次循环都到达边界,那么就应该在每一个for循环之后都立刻更新left、rigth、top、bottom的值,而不能全都放在四个循环后面。
public int[][] generateMatrix(int n) {
int num = 1;
int length = n - 1;
int[][] res = new int[n][n];
int left = 0, right = n - 1, top = 0, bottom = n - 1;
while (left <= right && top <= bottom) {
if (left == right&&top == bottom){
res[left][left] = num;
}
for (int j = left; j < right; j++) {
res[top][j] = num++;
}
for (int i = top; i < bottom; i++) {
res[i][right] = num++;
}
for (int j = right; j > left; j--) {
res[bottom][j] = num++;
}
for (int i = bottom; i > top; i--) {
res[i][left] = num++;
}
left++;
right--;
top++;
bottom--;
}
return res;
}