java基本排序算法汇总
排序也称为排序算法,排序是将一组数据,依指定的顺序进行排序的过程。
一、排序算法的种类
1.内部排序
指定需要处理的所有数据都加载到内部存储器(内存)中进行排序。
2.外部排序
数据量过大,无法加载到内存中,需要借助外部存储(文件等)进行排序。
3.常见的排序算法
二、时间复杂度
1.时间频度
算法中,语句执行次数称为时间频度。
2.有关时间频度的比较
- 忽略常数
- 忽略低次项
- 忽略系数
3.时间复杂度
算法中执行语句随着n趋于无穷时计算需要的时间级别。
- 常数阶 O(1)
- 对数阶 O(log2n)
- 线性阶 O(n)
- 线性对数阶 O(nlog2n)
- 平方阶 O(n^2)
- 立方阶 O(n^3)
- k次方阶级 O(n^k)
- 指数阶 O(2^n)
三、空间复杂度
算法执行过程中需要的内存的级别。
一般算法的优化可以通过空间换时间,减少执行时间。
四、八种算法分析
1.冒泡排序
1.思路:
/**
* 冒泡排序-交换排序
* 1.时间复杂度 0(n^2)
* 2.注意要点:
* >两层for循环 -两层for循环与每次将相对最大的值或最小的值放置到后面引起数组需要遍历个数的减少 变化一致。
* 第一层:共需要几次数组内部交换 :arr.length -1 次
* 第二层:每层循环有都少个元素要交换 ---随着层数变化,交换的次数是逐渐递减的
* 循环中的判断条件与 arr[i] 、arr[i+1]还是 arr[i-1] 、arr[i]这些有关
* 3. 关于优化问题:当有一层循环没有进行交换时,可以提前结束循环。
* 但需要注意:引入的变量需要足以 再次赋值。
*
* @param arr 冒泡排序数组
*/
2.图解分析:
3.代码:
public static void bubbleSort(int[] arr){
//优化:
boolean flag = false;
for (int i = 0; i < arr.length -1 ; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
int temp = arr[j];
if(arr[j] > arr[j+1] ){
flag = true;
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
if(flag == true){
flag = false;
}else{
break;
}
}
/*
方式一;
for (int i = 0; i < arr.length -1 ; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
int temp = arr[j];
if(arr[j] > arr[j+1] ){
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
*/
/*
//方式二:
for (int i = 0; i < arr.length -1 ; i++) {
for (int j = 1; j < arr.length - i; j++) {
int temp = arr[j-1];
if(arr[j-1] > arr[j] ){
arr[j-1] = arr[j];
arr[j] = temp;
}
}
}
*/
}
}
2.选择排序
1.思路:
/**
* 选择排序:
* 1.思路:将最小的数依次放入数组的最前部分,直到末尾。
* 2.分析:
* >首先选定下标为0的数组作为最小的值变量min以及定义Index下标,遍历到末尾,如果有比最小变量min
* 小的数,将下标赋值给index,以及它的值赋给min,<再用重新赋值的min、index 变量>继续比较
* 直至末尾。
* >得到最小的下标,通过下标交换最小的数。
* >下一轮循环从最小的数的下一位开始,(之前的最小的数无须比较)
* 继续得到最小的min、index,在交换的到最小的数
* 3.冒泡与选择排序区别:(位置具有相对性)
* >冒泡排序交换的是数组的数,选择排序是先交换最小下标,在通过下标交换数组的数
* >冒泡排序是将需要排序的值放在最后,选择排序则是放在最前。
*
* 4.关键点:
* >1.大循环一共 arr.length -1 次,
* >2.小循环从i 开始 到 末尾(arr.length -1)结束 。
* >3.易错点:每轮的最小值、下标指针 要更新。
*
* @param args
*/
2.图解分析:
3.代码::
public static void main(String[] args) {
int[] arr = new int[]{1,6,3,8,-2,-9};
selectSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void selectSort(int[] arr){
int min;
int minIndex;
for (int i = 0; i < arr.length -1 ; i++) {
min = arr[i];
minIndex = i;
for (int j = i+1; j < arr.length ; j++) {
if (min > arr[j]){
min = arr[j];
minIndex = j;
}
}
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
3.插入排序
1.思路:
/**
* 插入排序:
* 1.思路;将待排序的数组分为 有序区 和 无序区,将无序区内第一位数插入有序区相应的的位置。
* 2.分析:将待排序的数组分为 有序区 和 无序区, 刚开始一个数将第一个看成有序区
* * 将无序区第一个数与 有序区内的最有一个比较,如果较大,有序区相比较的数
* * 往后移动一位, 依次比较直到无序区中计较的数,找到比它小的数,
* * 插入这个值得前面。
* * 如此一直插入,直到末尾。
* 3.关键的:1.有序区与无序区都需要指针
* 2.比较大小时,移动位置通过while循环来实现,下标不能越界
* 3.需要保持无序区比较的值,比较后插入相比较值前面。
* 4.外层循环次数为:arr.length-1 次
*
*
* @param args
*/
2.图解分析:
3.代码:
public static void main(String[] args) {
int[] arr = {-2,-6,0,4,6,2,6};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertSort(int[] arr){
int temp;
int index;
for (int i = 1; i < arr.length ; i++) {
temp = arr[i];
index = i -1;
while(index >= 0 && temp < arr[index]){
arr[index + 1] = arr[index];//往后移
index--;
}
arr[index+1] = temp;
}
}
}
4.希尔排序
1.思路分析:
/**
*希尔排序:
* 1.思路;分组插入排序
* 2.分析: >1.将数组长度的一半作为原始的步长,每次缩减一半,直到步长为 1。
* >2.插入从每次的步长的位置开始到 数组末尾,且 每个位置都要向后探,
* 比较大小。然后在插入。
* 3.方式:1.交换法(效率低),2.移动法。
* 4.区别:插入排序与希尔排序区别:希尔排序没有 有序区和 无序区
* 5.相同点;都可以通过比较移动的方式快速插入。
* 6.注意点;移动法时;下标也会移动 且有边界 容易忽略
* 相比较的两个值一个是固定的,一个是移动的。
* 动与不动的值要注意。
*
* @param args
*/
2.图解分析:
3.代码:
public static void main(String[] args) {
int[] arr = {2,6,8,3,1,4,5,9,0,7};
shellSort1(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 移动法
*/
public static void shellSort1(int[] arr){
int index;
int temp;
for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
index = i;
temp = arr[i];
while(index - gap >= 0 && temp < arr[index - gap]){
arr[index] = arr[index - gap];
index -= gap;//往后移
}
arr[index] = temp;//插入
}
}
}
/**
* 交换法
* @param arr
*/
public static void shellSort(int[] arr){
for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap ; j >=0 ; j -= gap) {
if(arr[j + gap] < arr[j]){
int temp = arr[j + gap];
arr[j+ gap] = arr[j];
arr[j] = temp;
}
}
}
}
}
}
5.快排
1.思路:
/**
*快排;
* 1.思路;选定数组首个分组值pt,首位遍历,左边为小于等于pivot,右边为大于pivot,并且采用递归,依次分组
* 2.分析:采用递归,需要left right,arr 在left < right 下进行交i换
* >1.left为最左端,right为最右端,porit为数组arr[left]
* >2.因为递归需要用到left、rigth 所以 不能改变值,用 l 代替 left
* r 代替 right
* >3.对于 l 、r 都有边界,
* 1).对于 l 由于 arr[l] >= temp l++,l 必定大于等于 1
* 但是如果pivot为最小的,l 要不能 越界所以 l<r
* 2).对于r arr[r] < temp 所以 r 不会数组下标的越界,因为 r 始终表示
* 小于或等于povit的值的小标,因此 l<=r 中可以去等号,不然r移动不到在l前面
* l最值可能时大于povit的,但l 最终一定是小于等于povit的。
* 由后移动的 r 决定。
* >4.当l< r,退出循环时,r表示的一定时小于等于pivot值的下标,l时表示大于等于pivot值
* 的下标,所以pivot和arr[r] 要交换。
* >5.当退出while循环,之后的时左递归(left,r-1),和有递归(r+1,right)
* r+1 与 r -1 是当到最后时,保证 left 与 right 不相等,可以退出递归。
*
2.图解分析:
3.代码:
public class QuickSort {
public static void main(String[] args) {
int[] arr ={-3,-5,10,-3,5,7,2,-1,10,8,9};
quickSort(arr,0,arr.length -1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right){
if(left > right){
return;
}
int l = left;//因为下面递归要用到left 所以不能改变left的值
int r = right;
int base = arr[left];
while(l < r){//l<=r ,r一定是,指定到比base 小的值的位置,l>=r时,退出
while(arr[l] <= base && l < r ){//防止base为最小 l一路狂奔,下标越界,所以不取=号。
l++;
}
while(arr[r] > base && l <= r){//因为,l++,必定会执行,所以l>=1,r因此不会越界
r--;
}
if(l < r){//交换必须保证l<r,否则就退出,直接交换base 和 arr[r]
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
}
//将标杆值,与下标为r的位置,交换值
arr[left] = arr[r];
arr[r] = base;
quickSort(arr,left,r-1);//向左递归 必须r-1 当最后只用一个数时,必须要退出。
quickSort(arr,r+1,right);//向右递归 r+1
}
}
6.归并排序
1.思路分析:
/**
*
* 分治排序
* 1.思路:分:先将一个数组递归,通过二分法,将数组分成最小单位(分的目的是获取 每次递归拆分 即
* 在递归调用是独立栈内的变量的值 left right )
* 治:在数组分成最小单位下,将每层递归 所分成的两部分,通过指针
* 排序存储到 temp中,再将temp 中的每一个 通过 left 指定的
* 存储到 排序数组 arr中。
* 2.分析:分:通过二分法,递归分,需满足 left < right 如果取等,分到只有一个时
* 就出不来了
* 治:将需要合并的两部分,通过指针依次按顺序 存储到 temp, mid = (left + right) / 2
* i = left ; j = mid + 1
* 遍历中满足 i <= mid, j <= right;
*
* 当退出循环时,说明有一方 i或 j 越界 即全部存到了 temp
* 而另一方则没有全部存储完。判断条件 i <= mid、 j <= right
* 可以取等于好原因;没有全部取完。
*
*
*/
2.图解思路:
抽取—治 排序的过程
3.代码:
public class MergeSort {
public static void main(String[] args) {
int[] arr = {1,5,8,2,6,-3,0,7,-3,3,4,9};
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length - 1, temp);
System.out.println(Arrays.toString(arr));
}
public static void mergeSort(int[] arr, int left, int right,int[] temp){
int mid = (right + left) / 2;
if(left < right){//left 与 right 不同才可以分
mergeSort(arr, left, mid, temp);
mergeSort(arr,mid + 1, right, temp);
merge(arr, left, right, mid, temp);
}
}
public static void merge(int[] arr, int left, int right, int mid,int[] temp){
int i = left;
int j = mid + 1;
int t = 0;
while(i <= mid && j <= right){//当达到顶端时,必定有一个越出界
while(i <= mid && arr[i] <= arr[j]){
temp[t] = arr[i];
i++;
t++;
}
while(j <= right && arr[j] < arr[i]){
temp[t] = arr[j];
j++;
t++;
}
}
//当跳出时有一个,只要是那个没有越出界即 i>mid j > right,就没有遍历完
while(i <= mid){
temp[t] = arr[i];
t++;
i++;
}
while(j <= right){
temp[t] = arr[j];
t++;
j++;
}
/**
* 因为temp 只是临时的储存数组,简单进行了排序,
* 要根据 left 储存到arr,temp,只是一段简单排序的片段,根据
* left 可以整合到arr,变成完整的。
*/
int leftIndex = left;
t = 0;//从头遍历temp
while(leftIndex <= right){
arr[leftIndex++] = temp[t++];
}
}
}
7.基数排序
1.思路:
/**
*
* @param arr
* 基数排序
* 1.思路:找到最大位数的数字,根据它的位数来循环,存入桶内,在取出到数组,通过
* 反复储存,到最后达到排序目的。
* 2.分析:>1.建立二维数组bucket[10][arr.length],以及 每个桶中的个数counts[num]
* >2.找到最大位数的数字,根据它的位数来循环,获取每个数位置上的数字-使用求余的方法
* 求余 - 将数字处于 10的倍数得到 个位数位置 为该轮循环的位数。再求 %
* >3.通过获取的数字 放入相应的桶中。
* >4.依次从0 - 9 编号的桶中取出
* >5.对counts[num]在相应的桶(num,编号,遍历完)取完后 归零
* 3.核心思想:必须为正数,否则此代码失效
*
*/
2.图解分析:
3.代码:
public class RadixSort {
public static void main(String[] args) {
int[] arr = {0,1,4,65,333,995,26};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr){
//创建桶
int[][] bucket = new int[10][arr.length];
//获取最大的数
int max = arr[0];
for(int i = 1; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
}
//获取最大数字的位数
int maxLength = (max+"").length();
int num = 0;
int index ;
int[] counts = new int[10];//记录编号为0-9 个桶的个数
for(int i = 0, n = 1; i < maxLength; i++, n *= 10){
index = 0;
//存
for (int j = 0; j < arr.length; j++) {
num = arr[j] / n % 10;//获取需要位置的数字
bucket[num][counts[num]++] = arr[j];//count[num] 从零开始,取完要归零。
}
//取
for(int k = 0 ; k < 10; k++){
for(int l = 0; l < counts[k]; l++){
arr[index++] = bucket[k][l];//index从0开始,到counts[k]结束,因此不用去处理上一轮的多余数字
}
counts[k] = 0;//必须归零 k 为0-9 桶中的一个桶的编号
}
}
}
}
for(int i = 0, n = 1; i < maxLength; i++, n *= 10){
index = 0;
//存
for (int j = 0; j < arr.length; j++) {
num = arr[j] / n % 10;//获取需要位置的数字
bucket[num][counts[num]++] = arr[j];//count[num] 从零开始,取完要归零。
}
//取
for(int k = 0 ; k < 10; k++){
for(int l = 0; l < counts[k]; l++){
arr[index++] = bucket[k][l];//index从0开始,到counts[k]结束,因此不用去处理上一轮的多余数字
}
counts[k] = 0;//必须归零 k 为0-9 桶中的一个桶的编号
}
}
}
}