算法 基于数据交换的排序
一、时间复杂度为o(n^2)
1.1 冒泡排序
- 每进行一轮冒泡排序就将最大值放到后面
- 需每次比较相邻的值取得最大值
public static void sort(int[] array){
int len = array.length;
for (int i = 0; i < len - 1; i++) {
for(int j = 0; j < len - i - 1; j++){
if(array[j+1] > array[j]){
int mid = 0;
mid = array[j+1];
array[j+1] = array[j];
array[j] = mid;
}
}
}
}
1.2 选择排序
- 第一轮选择最小的数与第一个位置的数进行交换
- 第二轮选择最小的数与第二个位置的数进行交换
- …
思路:每轮找到最小的值与前面的值进行交换
public static void sort(int[] list){
for (int i = 0; i < list.length; i++) {
int tmp = list[0];
int index = 0;
for (int j = 0; j < list.length - i ; j++) {
if(list[j] > tmp){
tmp = list[j];
index = j;
}
}
int tmp1 = list[index];
list[index] = list[list.length - i - 1];
list[list.length - i - 1] = tmp1;
}
}
1.3 插入排序
- 从第二个数开始,将数插到前面合适的位置,当前数的位置是index,从前一个数开始,依次与当前数进行比较,大于当前数,往后移动一位,当前数占坑。
特点:
- 假设要插入的数时A,则排序的是A及A以前的数,后面的数涉及不到
- 可能需要移动大量数据
时间复杂度:o(n^2)
public static void sort(int[] arrays) {
for (int i = 1; i < arrays.length; i++) {
int tmp = arrays[i];
for (int j = i-1; j >=0; j--) {
if(arrays[j] > tmp){
arrays[j+1] = arrays[j];
arrays[j] = tmp;
}else {
continue;
}
}
}
}
- 第二种好理解
public static void sort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int j = i - 1 ;
while (j >= 0 && arr[j] > arr[i]) {
j--;
}
if(j < 0){
int tmp = arr[i];
for(int k = i - 1; k >= 0 ; k --){
arr[k+1] = arr[k];
}
arr[0] = tmp;
}else {
int tmp = arr[i];
for(int k = i - 1; k > j ; k --){
arr[k+1] = arr[k];
}
arr[j + 1] = tmp;
}
}
}
二、时间复杂度为o(N*logN)的算法
2.1 归并排序
- 先把数组拆分
- 分好后进行合并运算
注意:(int)3/2 = 1
public static void sort(int[] list, int start, int end){
if(end - start < 1){
return;
}
sort(list,start,(end+start)/2);
sort(list, (end+start)/2 + 1, end);
merge(list,start,(end+start)/2,(end+start)/2 + 1, end);
}
public static void merge(int[] list, int start1, int end1, int start2, int end2){
int[] tmp = new int[end2 - start1 + 1];
int s1 = start1;
int s2 = start2;
int i = 0;
while (s1 <= end1 && s2 <= end2){
if(list[s1] <= list[s2]){
tmp[i++] = list[s1++];
}else {
tmp[i++] = list[s2++];
}
}
while (s1 <= end1){
tmp[i++] = list[s1++];
}
while (s2 <= end2){
tmp[i++] = list[s2++];
}
for (int j = 0; j < tmp.length; j++) {
list[j + start1] = tmp[j];
}
}
参考:归并排序
2.2 快速排序
- 空间复杂度:o(logN)~o(N)
要点:
- 在程序第一步要判断(start < end),不然数组中只有一个元素时,会造成死循环。
- array[low] 赋值给array[high]时,也要判断high和low的关系。
- 判断数组边界时,尽量不要使用等于号,可能会造成栈溢出。
*已经排序号好的不需要再排序
赋值的时候一定要判断left<right
public static void sort(int[] list, int start, int end){
if(start >= end){
return;
}
int tmp = list[start];
int left = start;
int right = end;
while (left < right){
while (list[right] > tmp && left < right){
right --;
}
if(left < right) {
list[left++] = list[right];
}
while (list[left] < tmp && left < right){
left++;
}
if(left < right) {
list[right++] = list[left];
}
}
list[right] = tmp;
sort(list, start, right - 1);
sort(list, right + 1, end);
}
2.3 堆排序
思路:首先要进行堆初始化,即从最后一个非叶子结点调整堆,这样的结果是次大值维持在第二层,这样在节点数量减少时,可以从头结点初始堆。
public static void sort(int[] array) {
int length = array.length;
//从最后一个父节点往上调整堆,调整堆时,一个堆拥有的头结点数时总节点数的一半-1,从0开始
//调整初始堆
for (int i = length / 2 - 1; i >= 0; i--) {
adjustHeap(array, length, i);
}
//每次将头结点与最后一个节点进行交换
for (int i = length - 1; i >= 0; i--) {
int mid = array[i];
array[i] = array[0];
array[0] = mid;
adjustHeap(array,i,0);
}
}
/**
*
* @param array
* @param length 要调整的堆中元素的个数
* @param i 传过来的父节点
*
*/
private static void adjustHeap(int[] array, int length, int i) {
//调整的策略是:将一个父节点和两个子节点中最大的值给父节点
//2*i+1:父节点的左子节点,2*i+2:父节点的右子节点
//flag指向头结点
//index指向右子节点
int flag = i;
int index = 2*i + 1;
int value = array[i];
//比较两个子节点的较大值
while (index < length){
if(index + 1 < length){
if(array[index + 1] > array[index]){
index = index + 1;
}
}
if(array[flag] < array[index]){
int mid = array[flag];
array[flag] = array[index];
array[index] = mid;
flag = index;
index = 2*index + 1;
}else {
break;
}
}
}
2.4 希尔排序
- 插入排序的优化
需自定义规定步长,步长逐渐缩小,就能逐渐排序
public static void sort(int[] array) {
for (int i = 1; i <= 3; i++) {
foot(array,i);
}
}
public static void foot(int[] array, int n){
for (int i = n; i < array.length; i++) {
for(int j = i; j >= 0; j = j - n){
if(j - n >= 0 && array[j] > array[j - n]){
int mid = array[j-n];
array[j - n] = array[j];
array[j] = mid;
}
}
}
}
- 第二种解法
public static void sort(int[] arrays) {
for (int gap = arrays.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arrays.length; i++) {
int tmp = arrays[i];
for (int j = i - gap; j >= 0; j = j - gap) {
if (arrays[j] > tmp) {
arrays[j + gap] = arrays[j];
arrays[j] = tmp;
}else {
continue;
}
}
}
}
}
三、8种排序的比较
3.1 复杂度
3.2 参考
3.3 时间复杂度
- 冒泡排序最好的时间复杂度只需要比较一轮,因此最优的时间复杂度是o(n)。
- 选择排序最好的时间复杂度也要进行n轮比较,确定每轮的最小的值,因此是o(n^2)。
- 插入排序,最优每轮插入一个,无需移动,因此最优的时间复杂度是0(n)
- K是整数的范围
- 桶排序不了解,就记住一个计数和基数