一:冒泡排序
算法描述:
首先从数组的第一个元素开始到数组最后一个元素为止,对数组中相邻的两个元素进行比较,如果位于数组左端的元素大于数组右端的元素,则交换这两个元素在数组中的位置,此时数组最右端的元素即为该数组中所有元素的最大值。接着对该数组剩下的n-1个元素进行冒泡排序,直到整个数组有序排列。算法的时间复杂度为O(n^2)。是一个稳定的算法。
//冒泡排序 遇到比自己大的则交换 把最大 次大 次次大.....分别确定好
public static void bubbleSort(int []arr) {
int temp = 0;
for(int i = 0;i < arr.length - 1;i ++) {
boolean flag = true;
for(int j = 0;j < arr.length - 1 - i;j ++) {
if(arr[j] > arr[j + 1]) {
flag = false;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if(flag) break;
}
}
二:直接选择排序 :
算法描述:
每一趟在n-i+1(i=1,2,...,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。算法的时间复杂度为O(n^2)。是一个不稳定的算法。举个例子:序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了
//选择排序:依次选择最小的放在第一位 第二位....
public static void selectSort(int []arr) {
for(int i = 0;i < arr.length - 1;i ++) {
int idx = 0,min = Integer.MAX_VALUE;
for(int j = i;j < arr.length;j ++) {
if(arr[j] < min) {
min = arr[j];
idx = j;
}
}
int temp = arr[i];
arr[i] = arr[idx];
arr[idx] = temp;
}
}
三:插入排序
算法描述
将无序序列插入到有序序列中。算法的时间复杂度为O(n^2)。是一个稳定的算法。
//插入排序 从已排序的最后一个数字开始比较 比自身大的数向后挪一位,直到找到合适位置则插入
public static void insertSort(int []arr) {
for(int i = 1;i < arr.length;i ++) {
int cur = arr[i];
int idx = i - 1;
while(idx >= 0 && arr[idx] > cur) {
arr[idx + 1] = arr[idx];
idx --;
}
arr[idx + 1] = cur;
}
}
四:快速排序
算法描述:
通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序。
一趟快速排序的具体过程可描述为:从待排序列中任意选取一个记录(通常选取第一个记录)作为基准值,然后将记录中关键字比它小的记录都安置在它的位置之前,将记录中关键字比它大的记录都安置在它的位置之后。这样,以该基准值为分界线,将待排序列分成的两个子序列。时间复杂度为O(nlogn)。是一个不稳定的算法。
//快速排序:先让数组部分有序(左部分小于右部分),再递归调用对左右两部分分别进行快速排序。
public static void quickSort(int []arr,int left,int right) {
if(left > right) return;
//选一个基准值
int base = arr[left];
//定义变量i指向最左边,j指向最右边
int i = left,j = right;
while(i != j) {
//从右到左找第一个比基准值小的数
while(i < j && arr[j] >= base) j --;
//从左到右找第一个比基准值大的数
while(i < j && arr[i] <= base) i ++;
//找到后进行交换
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//i 和 j相遇,交换基准值与相遇位置的值
arr[left] = arr[i];
arr[i] = base;
//对左右两部分进行快速排序
quickSort(arr,left,i - 1);
quickSort(arr,i+1,right);
}
五:归并排序
算法描述:
“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序表。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并如此重复,直到得到一个长度为n的有序序列。时间复杂度为O(nlogn)。是一个稳定的算法。
//归并排序:自顶向下递归使部分有序,再使自底向上merge使得部分有序变成整体有序
public static void _merge(int []arr,int left,int mid,int right) {
//复制原数组 便于把结果数组直接放入原数组中
int []temp = new int [right - left + 1];
for(int i = left;i <= right;i ++) {
temp[i - left] = arr[i];
}
// i指向左部分有序数组的第一个元素
// j指向右部分有序数组的第一个元素
// k指向元素组的第一个位置 每次将当前的最小元素放到位置k
int i = left,j = mid + 1;
for(int k = left;k <= right;k ++) {
if(i > mid) {//左数组已经取完 直接取右数组
arr[k] = temp[j - left];
j ++;
}else if(j > right) {//右数组已经取完 直接取左数组
arr[k] = temp[i - left];
i ++;
}else if(temp[i - left] < temp[j - left]) {//左右两数组均有元素 取最小值
arr[k] = temp[i - left];
i ++;
}else {
arr[k] = temp[j - left];
j ++;
}
}
}
public static void mergeSort(int []arr,int left,int right) {
if(left >= right) return;
int mid = (left + right) >> 1;
//把数组分为左右两部分 直到数组部分有序(只有一个元素)
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
_merge(arr,left,mid,right); //自底向上归并左右两个有序数组
}
六:堆排序
算法描述:
堆看做是一个完全二叉树。并且,每个结点的值都大于等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于等于其左右孩子结点的值,称为小顶堆。
堆排序(Heap Sort)是利用堆进行排序的方法。其基本思想为:将待排序列构造成一个大顶堆(或小顶堆),整个序列的最大值(或最小值)就是堆顶的根结点,将根节点的值和堆数组的末尾元素交换,此时末尾元素就是最大值(或最小值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值(或次小值),如此反复执行,最终得到一个有序序列。时间复杂度为O(nlogn)。是一个不稳定的算法。
// 堆排序
//heapify 使得当前节点作为根节点的树成为一个堆
public static void heapify(int []tree,int n,int idx) {
if(idx >= n) return;
//比较当前节点与左右儿子的大小
int left = 2 * idx + 1;
int right = 2 * idx + 2;
int max = idx;
if(left < n && tree[left] > tree[max]) max = left;
if(right < n && tree[right] > tree[max]) max = right;
if(max != idx) {
int temp = tree[idx];
tree[idx] = tree[max];
tree[max] = temp;
heapify(tree,n,max);
}
}
public static void build_heap(int []tree) {
int n = tree.length - 1;
int lastParent = (n - 1) / 2;
//从最后一个父节点开始 分别向前对所有节点做一次heapify
for(int i = lastParent;i >= 0;i --) {
heapify(tree,n,i);
}
}
public static void heapSort(int []tree) {
//使数组形成一个堆
build_heap(tree);
for(int i = tree.length - 1;i >= 0;i --) {
// 堆顶为最大值 每次把堆顶与最后一个值交换 即保存在最后一位
int temp = tree[0];
tree[0] = tree[i];
tree[i] = temp;
//交换后 进行heapify操作形成一个新的堆
//砍掉后边已排好序的部分
heapify(tree,i,0);
}
}
最后,对算法的稳定性做一个总结:
稳定:冒泡排序、插入排序、归并排序和基数排序
不稳定:选择排序、快速排序、希尔排序、堆排序