冒泡排序
(1)思路:
-
从第一个石子开始,让它和右边相邻的石子进行比较,如果左边的石子大于右边的石子,那么就交换两个石子的位置,(也可以左小于右交换,这里采用大于交换),这样每比较一次,大的就跑到右边,直到跑到最右边。
-
起始时,左下标指向第一个石子,右下标指向第二个石子,然后比较
-
然后左右下标同时向右移动,再次比较
-
每次比较完,右下标指向的石头就是已经比较过的元素中的最大元素
-
这是第一趟排序,经过这趟排序之后,最大的就在最右边了,也就是排好序了
-
排序共进行几趟:N-1
(2)代码:
- 第二层就是控制你第 i+1趟(因为i从0开始)所比较的次数,第 i +1 趟比较了 N – 1 -i 次
(3)时间复杂度:O(n^2)
(4)稳定性
当你原来待排的元素中间有相同的元素,在没有排序之前它们之间有先后顺序,在排完后它们之间的先后顺序不变,我们就称这个算法是稳定的
- 冒泡排序是一个稳定排序:因为在交换的时候,如果两个石子相同,那么就不交换 [if (arr[j] > arr[j+1]){ 交换}],相同元素不会因为算法中哪条语句相互交换位置的。
(5)优化
- 假如从开始的第一对到结尾的最后一对,相邻的元素之间都没有发生交换的操作,这意味着右边的元素总是大于等于左边的元素,此时的数组已经是有序的了,我们无需再对剩余的元素重复比较下去了。
public static int[] bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
boolean flag = true;
for (int j = 0; j < n -i - 1; j++) {
if (arr[j + 1] < arr[j]) {
flag = false;
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
//一趟下来是否发生位置交换
if(flag)
break;
}
return arr;
}
- 采用的是冒泡排序内外循环一加一减,这样就可以简化越界问题(√)
public static int[] bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
int n = arr.length;
boolean flag = true;
for (int i = 0; i < n && flag; ++i) {
flag = false;
for (int j = n - 1; j > i; --j) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
flag = true;
}
}
}
}
选择排序
(1)思路:
不断地从未排序的元素中选择最大(或最小)的元素放入已排好序的元素集合中,直到未排序中仅剩一个元素为止。
- 先从这些元素中选出一个最小的(或最大的),和第一个元素进行交换,这样第一个元素就是最小的,第一个元素位置就变成有序区间了
- 同理,在剩下的无序区间选择最小的元素,将最小元素与无序区间的第一个元素进行交换,交换后原来无序区间的第一个元素就变为有序区间的最后一个元素了,有序区间递增一。
- 选出最小的一个元素:先随便选一个元素假设它为最小的元素(默认为无序区间第一个元素),然后让这个元素与无序区间中的每一个元素进行比较,如果遇到比自己小的元素,那更新最小值下标,直到把无序区间遍历完,那最后的最小值就是这个无序区间的最小值
(2)代码:
(3)时间复杂度:O(n^2)
(4)稳定性
- 由于选择元素之后会发生交换操作,所以有可能把前面的元素交换到后面,所以不是稳定的排序
插入排序
插入排序是一种比较简单直观的排序算法,适用处理数据量比较少或者部分有序的数据
(1)思路:
- 先将最后边的9和7比较,如果9大于7,则再拿8和7比较,直到找到不大于7(小于等于)的一张牌为止,然后将7插入到这张牌后面。
- 直接插入排序,就是把未排序的元素一个一个地插入到有序的集合中,插入时就像你那样,把有序集合从后向前扫一遍,找到合适的位置插入
(3)代码:
- 用for循环从前到后遍历整个数组,将无序元素一个一个地插入到正确的位置(排好序的位置),第一个元素认为它是排好序的,所以从第二个元素开始遍历。
- inserToRightPosition():可以用一个临时变量把待插元素(将要插入到有序集合的元素)存起来,然后逐个和有序集合里的元素比较,如果集合里的元素大于待插元素,就将它向后移动一个单元,这样当遇到有序集合中小于等于待插元素的元素时就有地方放待插元素了
- i 指向待插元素,j 会遍历有序数组中所有元素,直到找到合适的位置将待插元素(inserted)插入
public static void insertionSort(int [] arr) {
for (int i = 1; i < arr.length; i++) {
// 将 arr[i] 插入到正确的位置
inserToRightPosition(arr, i);
}
}
private static void inserToRightPosition(int[] arr, int i) {
// 备份待插元素
int inserted = arr[i];
int j = i - 1;
for(; j >= 0 && arr[j] > inserted; j--) {
arr[j + 1] = arr[j]; // 将比待插元素大的元素后移
}
// 将待插元素插入正确的位置
arr[j + 1] = inserted;
}
(4)时间复杂度:O(n^2)
- 最坏时间复杂度(所有元素倒序)
(5)稳定性
- 是稳定的,因为在比较的时候,这两个数相等的话,不会进行移动,前后两个数的次序不会发生改变