①冒泡排序:量量比较待排序数据元素的大小,发现两个数据元素的次序相反时进行交换,直到没有反序的数据元素为止。时间复杂度是O(n*2)。稳定的。下面给出两种排序算法,我比较喜欢第二种,因为第二种才能真正解释冒泡的原理
public class bubble1 {
public static void main(String[] args) {
int[] array = { 7, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
bubbleSort(array);
printArray(array);
}
private static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
public class bubble2 {
public static void main(String[] args) {
int[] array = { 7, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
bubbleSort(array);
printArray(array);
}
private static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = array.length - 2; j >= i; j--) {
if (array[j] > array[j + 1]) {//从倒数第二个往前一直到第i个,如果有反序的,则调换位置,就这样小的始终在前面,最后把最小的挤到第i个后面,i往后递增
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
②选择排序:每一趟从待排序的数据元素中选出最小(最大)的一个元素,顺序放在已经排好序的数列的最后面,
直到全部待排序的数据元素排完,算法复杂度是O(n*2)。不稳定的。
public class select {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
selectSort(array);
printArray(array);
}
private static void selectSort(int[] array) {
int k = 0;
for (int i = 0; i < array.length; i++) {
k = i;
for (int j = i; j < array.length; j++) {//从第i个元素往后顺延,找出第i个元素及其后边最小的元素,放在第i个元素的位置上
if (array[j] < array[k]) {
k = j;//保证k最终是第i个元素后边最小的元素的索引
}
}
int temp = array[i];
array[i] = array[k];
array[k] = temp;
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
③插入排序:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
算法的时间复杂度是o(n*2)。稳定的。
public class insert {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
insertSort(array);
printArray(array);
}
private static void insertSort(int[] array) {
int temp = 0;
for (int i = 1; i < array.length; i++) {
temp = array[i];//记录第i个元素
int j = 0;
for (j = i; j > 0; j--) {
if (array[j - 1] > temp) {//如果第i个元素前面的元素大于i元素,则将前面的元素后移
array[j] = array[j - 1];
} else {//如果i元素前边的元素比i元素大,则跳出循环,因为i元素之前的元素都是已经顺序排列好的
break;
}
}
array[j] = temp;
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
④快速排序:在当前无序区 R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为 X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和 R[I+1..H],
且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准 X 则位于最终排序的位置上,
即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当 R[1..I-1]和 R[I+1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。
最理想情况算法时间复杂度 O(nlogn),最坏O(n^2) 。不稳定。
public class Quick {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 0, 2, 9, 6, 4 };
printArray(array);
quickSort(array);
printArray(array);
}
private static void quickSort(int[] array) {
quick(array, 0, array.length - 1);
}
private static void quick(int[] array, int low, int high) {
int key = 0;
if (low < high) {
key = partition(array, low, high);
quick(array, low, key - 1);
quick(array, key + 1, high);
}
}
private static int partition(int[] array, int low, int high) {
int key = array[low];
while (low < high) {
while (low < high && array[high] >= key) {
high--;
}//这里是为了找出右边小于key的值,
swap(array, low, high);//将右边小于Key的值与第一个值交换
while (low < high && array[low] <= key) {
low++;
}//这里是为了找出左边大于Key的值
swap(array, low, high);//将刚刚交换的右边的high索引所在的值与现在得出的大于Key的值进行交换
}最终low索引所在的值的嘴边的值都小于array[low],右边的值都大于array[low]
return low;
}
private static void swap(int[] array, int low, int high) {
int temp = array[low];
array[low] = array[high];
array[high] = temp;
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
⑤堆排序:堆排序是一树形选择排序,在排序过程中,将 R[1..N]看成是一颗完全二叉树的顺序存储结构,
利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。 算法时间复杂度O(nlogn)。不wend
public class Heap {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
heapSort(array);
printArray(array);
}
private static void heapSort(int[] array) {
if (array == null || array.length < 0) {
return;
}
buildMaxHeap(array);
for (int i = array.length - 1; i >= 1; i--) {
swap(array, 0, i);
maxHeap(array, i, 0);
}
}
private static void buildMaxHeap(int[] array) {
if (array == null || array.length < 0) {
return;
}
int half = array.length / 2;
for (int i = half; i >= 0; i--) {
maxHeap(array, array.length, i);
}
}
private static void maxHeap(int[] array, int heapSize, int index) {
int left = 2 * index + 1;
int right = 2 * index + 2;
int largest = index;
if (left < heapSize && array[left] > array[index]) {
largest = left;
}
if (right < heapSize && array[right] > array[largest]) {
largest = right;
}
if (index != largest) {
swap(array, index, largest);
maxHeap(array, heapSize, largest);
}
}
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
⑥希尔排序:其实就是用步长控制的插入排序,希尔排序通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而让数据项可以大幅度移动,
这样的方式可以使每次移动之后的数据离他们在最终序列中的位置相差不大,保证数据的基本有序,大大提升了排序速度,运算时间复杂度 N*logN,不稳定。
public class Shell {
public static void main(String[] args) {
int[] array = { 0, 8, 3, 1,1, 2, 9, 6, 4 };
printArray(array);
shellSort(array);
printArray(array);
}
private static void shellSort(int[] array) {
int j = 0;
int temp = 0;
for (int increment = array.length / 2; increment > 0; increment /= 2) {
for (int i = increment; i < array.length; i++) {
temp = array[i];
for (j = i; j >= increment; j -= increment) {
if (temp < array[j - increment]) {//这里实际上就是插入排序,只是按照规定的步长来插入排序
array[j] = array[j - increment];
} else {
break;
}
}
array[j] = temp;
}
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
⑦归并排序:Divide: 把长度为 n 的输入序列分成两个长度为 n/2 的子序列。Conquer: 对这两个子序列分别采用归并排序。
Combine: 将两个排序好的子序列合并成一个最终的排序序列。时间复杂度是 O(nlogn)。 稳定的 。
public class merge {
public static void main(String[] args) {
int[] array = { 7, 8, 3, 1, 5, 2, 9, 6, 4 };
printArray(array);
mergeSort(array);
printArray(array);
}
private static void mergeSort(int[] array) {
sort(array, 0, array.length - 1);
}
private static void sort(int[] array, int left, int right) {
if (left >= right) {
return;
}
int center = (left + right) / 2;
sort(array, left, center);
sort(array, center + 1, right);
mergesort(array, left, center, right);
}
private static void mergesort(int[] array, int left, int center, int right) {
int[] tempArray = new int[array.length];
int mid = center + 1;
int third = left;
int temp = left;
while (left <= center && mid <= right) {
if (array[left] <= array[mid]) {//这里就相当于二路归并排序,
tempArray[third++] = array[left++];
} else {
tempArray[third++] = array[mid++];
}
}
while (left <= center) {
tempArray[third++] = array[left++];
}
while (mid <= right) {
tempArray[third++] = array[mid++];
}
while (temp <= right) {
array[temp] = tempArray[temp++];
}
}
private static void printArray(int[] array) {
if (array.length == 0 || array == null) {
return;
}
for (int i = 0; i < array.length - 1; i++) {
System.out.print(array[i] + " ");
}
System.out.println(array[array.length - 1]);
}
}
2、快速排序的平均时间复杂度是多少?最坏时间复杂度是多少?在哪些情况下会遇到最坏的时间复杂度。
快速排序的平均时间复杂度 O(nlogn),最坏时间复杂度 O(n^2)。
最坏情况:待排序的序列为正序或者逆序。每次划分只得到一个比上一次划分少一个记录的子序列,另一个为空。如果递归树画出来,它就是一棵斜树。
当每次 pivot 选择恰好都把列表元素分成了(1,n-1)。
采取措施:pivot 的选取是通过 random 来进行 。
各个排序算法的稳定性,并给出理由。
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。