1、插入排序
-
直接插入排序
时间复杂度O{n^2}
package 排序.插入排序.直接插入排序;
/**
* 插入排序 -- 每次从后面选择一个数与前面排好序的数组进行比较,如果小,进行交换并与前面继续比较,如果大学,结束,并且不交换
* 两层for循环
* 第一层: 确定有序数组,
* 第二层: 使用下一个数与前面的有序数组比较,确定合适的位置
*/
public class InsertSorted {
/**
* 这个方法每次比较成功都会执行三次赋值,性能略差
* @param arr
*/
public static void sort(int[] arr) {
for (int i = 1; i < arr.length; i++) { //从1开始,因为第一个arr[0]已经有序
for (int j = i; j >=1 && arr[j] < arr[j-1]; j--) { //不断与前面有序数组比较,确定合适位置
int temp = arr[j]; //执行交换
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
//时间复杂度 O{n^2}
/**
* 改进的插入排序
* @param
*/
public static void sort2(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int j; //保存可以插入的位置
int temp = arr[i]; //需要插入的元素
for (j = i; j >=1 && temp < arr[j-1]; j--) { //不断与前面有序数组比较,确定合适位置
arr[j] = arr[j-1]; //向后赋值
}
arr[j] = temp;
}
}
public static void main(String[] args) {
int[] arr = {10, 9, 8, 7, 50, 5, 4, 3, 2, 1};
InsertSorted.sort2(arr);
for(int i = 0; i < arr.length; i ++) {
System.out.print(arr[i] + " ");
}
}
}
-
折半插入排序
时间复杂度O{n^2}(查找次数减少,移动次数不变)
package 排序.插入排序.折半插入排序;
public class InsertSorted {
public static void sort(int[] arr) {
for (int i = 1; i < arr.length; i ++) {
int temp = arr[i]; //定义临时变量保存需要插入的值
int low = 0, high = i - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (temp < arr[mid]) high = mid - 1; //条件没有小于,如果有会发生不稳定
else low = mid + 1;
} //high的后一个位置就是插入的位置
//需要插入的位置是high+1,
for (int j = i; j > high + 1; j --) {
arr[j] = arr[j-1];
}
arr[high + 1] = temp;
}
}
/**
* 时间复杂度,O(n^2)
* 虽然采用了折半插入,但是仅仅在查找过程中时间复杂度为O(log2n),因为减少了关键字的比较
* 但是在数据移动时的复杂度任然为O(n^2),
* 但是在性能上还是优于直接插入排序
* @param args
*/
public static void main(String[] args) {
int[] arr = {10, 9};
InsertSorted.sort(arr);
for(int i = 0; i < arr.length; i ++) {
System.out.print(arr[i] + " ");
}
}
}
-
希尔排序(缩小增量排序)
增量一般为n/2…
时间复杂度约为O{n^1.3}
package 排序.插入排序.希尔排序;
/**
* 希尔排序:缩小增量排序,(不稳定)
* 原理:直接插入排序在 (基本有序,记录较少) 的情况下有较好的性能
*/
public class InsertSorted {
public static void sort(int[] arr) {
int d = arr.length / 2; //这就是缩小增量的过程
while (d > 0) {
for (int i = d; i < arr.length; i ++) {
int temp = arr[i];
int j;
for (j = i; j >= d && temp < arr[j - d]; j = j - d) {
arr[j] = arr[j - d]; //后移
}
arr[j] = temp;
}
d = d / 2 ;
}
}
public static void main(String[] args) {
int[] arr = {10, 9, 8, 7, 50, 5, 4, 3, 2, 1};
InsertSorted.sort(arr);
for(int i = 0; i < arr.length; i ++) {
System.out.print(arr[i] + " ");
}
}
}
2、交换排序
-
冒泡排序(几乎不用)
时间复杂度O{n^2}
package 排序.交换排序.冒泡排序;
public class ExchangeSorted {
/**
* 时间复杂度O(n^2),性能较差,交换次数较多,几乎不用
* @param arr
*/
public static void sort(int[] arr) {
boolean flag = true; //设置一个监视哨,如果在某一趟排序中未发生交换,说明该序列已经有序,可以跳出外层循环;
int m = arr.length;
for (int i = 0; i < arr.length && flag; i ++)
{
flag = false;
for (int j = 0; j < m - 1; j ++) { //第一趟选择最大的,第二趟选择次大的,以此类推。。。。。。
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true;
}
}
m--; //因为每一趟都会选出一个最大的值,所以不需要再次向后面进行比较
}
}
public static void main(String[] args) {
int[] arr = {10, 9, 8, 7, 50, 5, 4, 3, 2, 1};
ExchangeSorted.sort(arr);
for(int i = 0; i < arr.length; i ++) {
System.out.print(arr[i] + " ");
}
}
}
-
快速排序
时间复杂度O{nlog2n}(2为底)
package 排序.交换排序.快速排序;
/**
* 时间复杂度O(log2n)
* 在冒泡排序中,一次交换只能消除一个逆序,而快速排序间隔多个记录进行交换可能消除多个逆序
* 原理:选择一个pivot(枢轴),经过一趟排序后,将小于pivot的放到pivot前面,将大于pivot的放到pivot后面,
* 就可以确定该pivot在数组中的位置(通常选择第一个记录为pivot)\\
* 适用于初始无序,n较大时
* (不稳定排序)
*/
public class QuickSorted {
public static void sort(int[] arr ,int start, int end) {
int left = start,right = end;
int pivot = arr[left];
if (left >= right) return; //递归出口的条件,当区间大于零的时候才继续执行
while (left < right) { //一趟排序,结束条件是当left指针和right指针相遇
//必须从右边开始扫描,因为左端的值已经放到了临时变量pivot中,可以进行覆盖,
//如果从左边开始扫描,就会将右端的值覆盖,发生丢失
while (left < right && arr[right] >= pivot) right --; //如果右边的值大于等于pivot,指针左移
arr[left] = arr[right];
while (left < right && arr[left] <= pivot) left ++; //如果左边的值小于等于pivot,指针右移
arr[right] = arr[left];
}
arr[left] = pivot; //此时left = right,这个位置就是pivot(arr[start])应该在的位置
sort(arr, start, left - 1); //递归调用函数,这是左区间
sort(arr, left + 1, end); //递归调用函数,这是右区间
}
public static void main(String[] args) {
int[] arr = {10, 9, 8, 7, 50, 5, 4, 3, 5, 1};
sort(arr,0,arr.length - 1);
for(int i = 0; i < arr.length; i ++) {
System.out.print(arr[i] + " ");
}
}
}
3、选择排序
-
简单选择排序
时间复杂度O{n^2}
package 排序.选择排序.简单选择排序;
/**
* 选择排序,每次都从未排序中选择最小的放到最前面,
* 利用两层循环
* 第一层for循环用于选择确定需要选择最小值的数量
* 第二层循环用于找出剩余数据的最小值
* 采用寻找最小值下标的方法
*/
public class SelectedSorted {
public static void sort(int[] arr) { //传入数组
for (int i = 0; i < arr.length; i++) { //第一层循环,每次都设当前循环位置为最小值位置
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) { //第二层循环,遍历除上一层循环位置后的所有值,
if (arr[j] < arr[minIndex]) { //与最小值位置的值相比较,不断找到最小值得位置,知道循环完成
minIndex = j;
}
}
int temp = arr[minIndex]; //定义临时变量,存放最小值
arr[minIndex] = arr[i]; //将第一层循环所在位置的值赋给当前最小值所在的位置,完成交换
arr[i] = temp; //将最小值赋给第一层循环所在位置
}
}
/**
* 时间复杂度 为 O{n^2}
* @param args
*/
public static void main(String[] args) {
int[] arr = new int[]{10, 9, 8, 7, 50, 5, 4, 3, 2, 1};
SelectedSorted.sort(arr);
for(int i = 0; i < arr.length; i ++) {
System.out.print(arr[i] + " ");
}
}
}
堆排序
时间复杂度O(nlog2n),空间复杂度O(1) package 排序.选择排序.堆排序;
public class HeapSorted {
/**
* 筛选法建立堆(大根堆)
* @param arr 传入的数组
* @param start 需要调整堆的起始位置
* @param end 需要调整堆的结束位置
* 我采用的是数组下标从零开始的数组,建立
* 的堆也是从0开始的,
* 所以(cur != 2*start)而是(cur = 2 * start + 1)
*/
public void heapAdjust(int[] arr, int start, int end) { //为数组的start位置到end位置调整堆
int temp = arr[start]; //使用临时变量保存arr[start](就是第一个元素)
int cur; //用来指向起始位置(start)的下一个位置
for (cur = 2 * start + 1; cur <= end; cur = 2 * start + 1) {
//当cur指向最后一个元素(cur = end)时,就不需要再判断arr[cur] < arr[cur + 1]了,因为没有arr[cur + 1],
//如果条件为cur < end && arr[cur] < arr[cur + 1],会发生数组越界
if (cur < end && arr[cur] < arr[cur + 1]) cur++;
if (temp >= arr[cur]) break; //如果temp(arr[start]>=arr[cur]),跳出循环,不再往下进行比较
arr[start] = arr[cur]; //否则元素上移
start = cur; //并且start位置下移
}
arr[start] = temp;
}
/**
* 建初堆
* @param arr (传入需要建初堆的数组)
*
* 建立完初堆后,根节点为最大值
*/
public void creatHeap(int[] arr) {
int n = arr.length;
//因为数组下标从0开始,所以(n / 2 - 1)为第一个需要调整堆的位置,大于(n / 2 - 1)的位置为叶子节点,符合堆的定义
for (int i = n / 2 - 1; i >= 0; --i) { //根节点也需要调整堆,所以i >= 0;
heapAdjust(arr, i, n - 1); //最后一个元素位置为 n -1 ;
}
}
/**
* 堆排序
* @param arr (需要堆排序的数组)
*/
public void heapSort(int[] arr) {
creatHeap(arr); //先建立初堆,现在堆得根结点为最大值
for (int i = arr.length - 1; i > 0; --i) {
//将最大值放入最后一个位置,并将最后一个元素放到堆顶,然后继续调整堆,不断将次大值放入后面相应的位置
//直到 i == 1,元素有序
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapAdjust(arr, 0, i - 1); //不断调整堆,每次减少最后一个元素位置
}
}
public static void main(String[] args) {
int[] arr = new int[] {0,5,9,6,5,8,7,15,2,789,56,145,17,26,0};
new HeapSorted().heapSort(arr);
for (int temp : arr) {
System.out.println(temp);
}
}
}
4、归并排序
-
二路归并排序
时间复杂度O{nlog2n}(n为底)
package 排序.归并排序.二路归并排序;
public class MSort {
/**
* 将arr[low~mid]和arr[mid+1~high]合并成一个有序数组
*
*/
public static void merge(int[] arr, int low, int mid, int high) {
int i = low, j = mid + 1, k = 0;
int[] nArr = new int[high - low +1]; //分配一个新的数组,用来作为临时变量,大小为需要归并的数组的元素个数(high-low+1)
while (i <= mid && j <= high) { //一个while循环,不断选出两边最小的数放到临时变量中
if (arr[i] <= arr[j]) nArr[k++] = arr[i++];
else nArr[k++] = arr[j++];
}
//选出剩下的一端的剩余数据放到临时数组变量尾部,因为不知道那一边先结束,所以设置了两个while循环
while (i <= mid) {
nArr[k++] = arr[i++];
}
while (j <= high) {
nArr[k++] = arr[j++];
}
for (int m = low,n = 0; m <= high; m ++, n++) { //使用一个for循环将临时变量数组的值赋给原始数组,位置为方法参数传进的位置
arr[m] = nArr[n];
}
}
//进行归并
public static void mergeSort(int[] arr ,int low, int high) {
if (low == high) return; //递归终止条件
int mid = (low + high) / 2;
mergeSort(arr,low,mid); //递归归并左部分,(包含mid)
mergeSort(arr,mid + 1, high); //递归归并右部分
merge(arr,low,mid,high);
}
public static void main(String[] args) {
int[] arr = {5,6,10,7,9,5,6,3,89,54,54,5,2,4,789,566,216,21,4,3446,246};
mergeSort(arr,0,arr.length - 1);
for (int temp : arr) {
System.out.print(temp + " ");
}
}
}