欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

程序员文章站 2022-06-04 15:01:01
...

1.冒泡排序

* 基本思想:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值

* 若发现逆序则交换,使值较大的元素逐渐从前向后移,就像水底的气泡一样逐渐向上冒

* 因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,那说明序列有序,

* 因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(冒泡【排序优化)

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序) 

 public static void BubbleSort(int[] n){
        for (int i = 0; i < n.length-1; i++) {
            for (int j = 0; j <(n.length-1)-i ; j++) {
                if(n[j]>n[j+1]){
                    int temp =n[j];
                    n[j] = n[j+1];
                    n[j+1] = temp;
                }
            }
        }
        System.out.println("n:"+ Arrays.toString(n));
    }
 public static void BubbleSortOptimize(int[] n){
        //,如果一趟比较下来没有进行过交换,那说明序列有序,
        boolean flag = false; //记录一趟是否发生改变
        for (int i = 0; i < n.length-1; i++) {
            for (int j = 0; j <(n.length-1)-i ; j++) {
                if(n[j]>n[j+1]){
                    flag = true;
                    int temp =n[j];
                    n[j] = n[j+1];
                    n[j+1] = temp;
                }
            }
            if(!flag){
                break;
            }else
            {
                flag = false;
            }
        }
    }

 

2.选择排序

*选择排序思想: 属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,(最小或者最大)再依规定交换位置后达到排序的目的。

* 说明

* 1.选择排序一共有数组大小-1 轮排序

* 2.每一轮排序,又是一个循环,循环的规则是

* 2.1先假定当前这个数是最小数

* 2.2然后和后面的每一个数进行比较,如果发现有比当前数更小的数,就重新却时那个最小数,并得到下表

* 2.3当遍历到数组的最后是,就得到本轮最小数和下标

* 2.4交换

* */

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

  public static void selectSort(int[] arr){
        for (int j = 0; j <arr.length-1 ; j++) {
            int minIndex = j;
            int minValue = arr[j];
            for (int i = j+1; i <arr.length ; i++) {
                if(minValue>arr[i]){
                    minValue = arr[i];
                    minIndex = i;
                }
            }
            if(minIndex != j){
                arr[minIndex] = arr[j];
                arr[j] = minValue;
            }

        }
        System.out.println(Arrays.toString(arr));
    }

3.插入排序

* 插入排序(InsertSorting):是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的

*

*思想:把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只包含一个元素,

* 无序表中包含有n-1个元素,排序过程中每个从无序表中取出第一个元素,

* 把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置, 使之成为新的有序表。

*

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

 public static void InsertSort(int[] arr){

        for (int i = 1; i <arr.length ; i++) {
            int insertVal = arr[i];  //定义待插入的数
            int insertIndex = i-1; //arr[i] 前面这个数的下标
            while(insertIndex>=0&& insertVal <arr[insertIndex]){
                //1.保证insertIndex >=0保证在给insertVal找插入位置,不越界
                //2.insertVal <arr[insertIndex] 待插入的数,还没有找到插入位置, 将arr[insertIndex]后移
                arr[insertIndex+1] = arr[insertIndex];
                insertIndex--;
            }
            //退出while循环时,说明插入的位置找到,insertIndex+1

            if(insertIndex+1 ==i){
                arr[insertIndex+1] = insertVal;
            }
             arr[insertIndex+1] = insertVal;
        }
    }

4. 希尔排序

*希尔排序时候为了解决 插入排序的缺点

 

* 插入排序的缺点: 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响。

*

 希尔排序(Donald Shell):也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也叫缩小增量排序

* 基本思想:把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词

*

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

 

	public static void shellSort(int[] arr) {
		
		int temp = 0;
		int count = 0;
		// 根据前面的逐步分析,使用循环处理
		for (int gap = arr.length / 2; gap > 0; gap /= 2) {
			for (int i = gap; i < arr.length; i++) {
				// 遍历各组中所有的元素(共gap组,每组有个元素), 步长gap
				for (int j = i - gap; j >= 0; j -= gap) {
					// 如果当前元素大于加上步长后的那个元素,说明交换
					if (arr[j] > arr[j + gap]) {
						temp = arr[j];
						arr[j] = arr[j + gap];
						arr[j + gap] = temp;
					}
				}
			}
			//System.out.println("希尔排序第" + (++count) + "轮 =" + Arrays.toString(arr));
		}
}	
//对交换式的希尔排序进行优化->移位法
	public static void shellSort2(int[] arr) {
		
		// 增量gap, 并逐步的缩小增量
		for (int gap = arr.length / 2; gap > 0; gap /= 2) {
			// 从第gap个元素,逐个对其所在的组进行直接插入排序
			for (int i = gap; i < arr.length; i++) {
				int j = i;
				int temp = arr[j];
				if (arr[j] < arr[j - gap]) {
					while (j - gap >= 0 && temp < arr[j - gap]) {
						//移动
						arr[j] = arr[j-gap];
						j -= gap;
					}
					//当退出while后,就给temp找到插入的位置
					arr[j] = temp;
				}

			}
		}
	}

5.快速排序

快速排序: 是对冒泡排序的一种改进。

思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按照此方法对两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

 

	public static void quickSort(int[] arr,int left, int right) {
		int l = left; //左下标
		int r = right; //右下标
		//pivot 中轴值
		int pivot = arr[(left + right) / 2];
		int temp = 0; //临时变量,作为交换时使用
		//while循环的目的是让比pivot 值小放到左边
		//比pivot 值大放到右边
		while( l < r) { 
			//在pivot的左边一直找,找到大于等于pivot值,才退出
			while( arr[l] < pivot) {
				l += 1;
			}
			//在pivot的右边一直找,找到小于等于pivot值,才退出
			while(arr[r] > pivot) {
				r -= 1;
			}
			//如果l >= r说明pivot 的左右两的值,已经按照左边全部是
			//小于等于pivot值,右边全部是大于等于pivot值
			if( l >= r) {
				break;
			}
			
			//交换
			temp = arr[l];
			arr[l] = arr[r];
			arr[r] = temp;
			
			//如果交换完后,发现这个arr[l] == pivot值 相等 r--, 前移
			if(arr[l] == pivot) {
				r -= 1;
			}
			//如果交换完后,发现这个arr[r] == pivot值 相等 l++, 后移
			if(arr[r] == pivot) {
				l += 1;
			}
		}
		
		// 如果 l == r, 必须l++, r--, 否则为出现栈溢出
		if (l == r) {
			l += 1;
			r -= 1;
		}
		//向左递归
		if(left < r) {
			quickSort(arr, left, r);
		}
		//向右递归
		if(right > l) {
			quickSort(arr, l, right);
		}
		
		
	}

6.归并排序

归并排序(Merge-sort):是利用归并的思想实现的排序方法,该方法采用经典的分治(divide-and-conquer)策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案“修补”在一起,即分而治之)

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

 

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

//分+合方法
	public static void mergeSort(int[] arr, int left, int right, int[] temp) {
		if(left < right) {
			int mid = (left + right) / 2; //中间索引
			//向左递归进行分解
			mergeSort(arr, left, mid, temp);
			//向右递归进行分解
			mergeSort(arr, mid + 1, right, temp);
			//合并
			merge(arr, left, mid, right, temp);
			
		}
	}
	
	//合并的方法
	/**
	 * 
	 * @param arr 排序的原始数组
	 * @param left 左边有序序列的初始索引
	 * @param mid 中间索引
	 * @param right 右边索引
	 * @param temp 做中转的数组
	 */
	public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
		
		int i = left; // 初始化i, 左边有序序列的初始索引
		int j = mid + 1; //初始化j, 右边有序序列的初始索引
		int t = 0; // 指向temp数组的当前索引
		
		//(一)
		//先把左右两边(有序)的数据按照规则填充到temp数组
		//直到左右两边的有序序列,有一边处理完毕为止
		while (i <= mid && j <= right) {//继续
			//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
			//即将左边的当前元素,填充到 temp数组 
			//然后 t++, i++
			if(arr[i] <= arr[j]) {
				temp[t] = arr[i];
				t += 1;
				i += 1;
			} else { //反之,将右边有序序列的当前元素,填充到temp数组
				temp[t] = arr[j];
				t += 1;
				j += 1;
			}
		}
		
		//(二)
		//把有剩余数据的一边的数据依次全部填充到temp
		while( i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到temp
			temp[t] = arr[i];
			t += 1;
			i += 1;	
		}
		
		while( j <= right) { //右边的有序序列还有剩余的元素,就全部填充到temp
			temp[t] = arr[j];
			t += 1;
			j += 1;	
		}
		
		
		//(三)
		//将temp数组的元素拷贝到arr
		//注意,并不是每次都拷贝所有
		t = 0;
		int tempLeft = left; // 
		//第一次合并 tempLeft = 0 , right = 1 //  tempLeft = 2  right = 3 // tL=0 ri=3
		//最后一次 tempLeft = 0  right = 7
		while(tempLeft <= right) { 
			arr[tempLeft] = temp[t];
			t += 1;
			tempLeft += 1;
		}
		
	}

7.基数排序

1.基数排序:属于“分配式排序(distribution sort)”又称“桶子法(bucket sort)”顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。

 

2.基数排序(Radix sort)是桶排序的扩展

 

3.基数排序是属于稳定性的排序,基数排序法是效率高的稳定排序法

 

4.基数排序是1887年赫尔曼发明的

 

1.基数排序:属于“分配式排序(distribution sort)”又称“桶子法(bucket sort)”顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。

 

2.基数排序(Radix sort)是桶排序的扩展

 

3.基数排序是属于稳定性的排序,基数排序法是效率高的稳定排序法

 

4.基数排序是1887年赫尔曼发明的

 

基本思想:1.将所有带比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)

//基数排序方法
	public static void radixSort(int[] arr) {
		
		//根据前面的推导过程,我们可以得到最终的基数排序代码
		
		//1. 得到数组中最大的数的位数
		int max = arr[0]; //假设第一数就是最大数
		for(int i = 1; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		//得到最大数是几位数
		int maxLength = (max + "").length();
		
		
		//定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
		//说明
		//1. 二维数组包含10个一维数组
		//2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
		//3. 名明确,基数排序是使用空间换时间的经典算法
		int[][] bucket = new int[10][arr.length];
		
		//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
		//可以这里理解
		//比如:bucketElementCounts[0] , 记录的就是  bucket[0] 桶的放入数据个数
		int[] bucketElementCounts = new int[10];
		
		
		//这里我们使用循环将代码处理
		
		for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
			//(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位..
			for(int j = 0; j < arr.length; j++) {
				//取出每个元素的对应位的值
				int digitOfElement = arr[j] / n % 10;
				//放入到对应的桶中
				bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
				bucketElementCounts[digitOfElement]++;
			}
			//按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
			int index = 0;
			//遍历每一桶,并将桶中是数据,放入到原数组
			for(int k = 0; k < bucketElementCounts.length; k++) {
				//如果桶中,有数据,我们才放入到原数组
				if(bucketElementCounts[k] != 0) {
					//循环该桶即第k个桶(即第k个一维数组), 放入
					for(int l = 0; l < bucketElementCounts[k]; l++) {
						//取出元素放入到arr
						arr[index++] = bucket[k][l];
					}
				}
				//第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
				bucketElementCounts[k] = 0;
				
			}
			//System.out.println("第"+(i+1)+"轮,对个位的排序处理 arr =" + Arrays.toString(arr));
			
		}
}

总结:

排序算法总结(冒泡、选择、插入、希尔、快速、归并、基数、堆排序)