大二时候学的数据结构,也没有好好实现一下,上个星期课上写选择排序,选择排序是啥来着,课下就整理了一下。
八种排序方法
常见的八种排序方法:冒泡排序 快速排序 插入排序 希尔排序 选择排序 堆排序 归并排序 基数排序
冒泡排序,快速排序都属于交换排序,插入希尔排序都是属于插入排序,希尔排序的基础也是插入排序,选择排序和堆排序都是属于选择排序。
各排序方法详细介绍
冒泡排序
冒泡排序是我最先听到的,感觉是最简单的一个排序方法了,就像这个名字一样,每一次遍历数组,都要把最大(或者最小)的数字放到最后面(或最前面)。
每次遍历数组时,依次比较相邻的数字大小,进行交换或者是不做处理继续向下比较,需要遍历arrays.len-1次。
过程如下图,红色数字代表本次冒泡的数字。
但是加入是一个有序序列 1,2,3,4,5,6,7那不也得依次依次遍历,没有必要,那么就加一个标记,如果某次遍历没有进行数字交换,那么就代表其实现在数组已经是有序的啦,不用再继续遍历,提前退出; 还有一个地方可以改进的就是,如果在第二次遍历的时候,最后一位数字已经是最大的了,没有必要再次比较,同样的最后一次遍历的时候,也只是前面两位数字需要进行比较,没有必要后面的再次遍历一次。
下面分别是未改进和改进后的代码:
public static void bubbleSort(int[] arrays){
int len = arrays.length;
for (int i = 0; i < len-1; i++) {
for (int j = 0; j < len - 1; j++) {
if (arrays[j] > arrays[j+1]){
swap(arrays,j,j+1);
}
}
}
}
public static void bubbleSort(int[] arrays){
int len = arrays.length;
for (int i = 0; i < len-1; i++) {
boolean flag = false;
for (int j = 0; j < len - i - 1; j++) {
if (arrays[j] > arrays[j+1]){
flag = true;
swap(arrays,j,j+1);
}
}
if (!flag) break;
}
}
快速排序
快速排序,和冒泡都是属于交换排序,快速排序用二分法不断把一个无序序列分为两半,调整之后成为有序序列,具体方法如下:
1.找基准数字(一般是第一个数字),对数组进行一次迭代交换之后,基准数字找到了自己的最终位置,即 基准数字左半边 都是比它小的,右半边都是比它大的。
2.基准数字左半边可以看作为一个新的无序数组,用步骤 1 的方法迭代一遍,又可以分成两部分,继续用同样的方法迭代,直到无法再分;基准数字右半边也可以看作一个新的无序数组,用步骤1的方法迭代一遍,也可以分为两部分,继续继续,直到无法在分。
3.现在数组就已经有序啦。
迭代交换时:在每次遍历数组的时候,一个指针i一个指针j,i指针负责找到比基准数字大的数儿,而j负责找到比基准数字小的数。每次遍历,基准数字由temp暂放,基准位为空位;先由j开始找,当找到比基准数字小的数字时,将其值赋给空位,之后i开始向后寻找,寻找到大于基准数则将其值赋给空位,之后再继续寻找j赋值给空位,继续寻找i赋值给空位……直到i>=j停止,将temp放入空位。
下图为基准数字找自己的最终位置,这次迭代过后,基准数字左<基准数字,右>基准数字,左右两部分可以进行下一次迭代啦
public static void quickSort(int[] arrays, int left, int right){
if (left >= right){
return;
}
int index = getMiddle(arrays, left, right);
quickSort(arrays,left,index-1);
quickSort(arrays,index+1,right);
}
public static int getMiddle(int[] arrays, int left, int right){
int index = arrays[left];//基准点
while(left < right){
while (arrays[right] >= index && right > left){
right--;
}
arrays[left] = arrays[right];
while(arrays[left] <= index && right > left){
left++;
}
arrays[right] = arrays[left];
}
arrays[right] = index;
return right;
}
插入排序
直接插入排序是:按一定的顺序,取无序序列的数字,插入到有序序列中,使得有序序列成为一个新的有序序列,直到无序序列取完为止。
public static void insertSort(int[] arrays){
int len = arrays.length;
for (int i = 1; i < len; i++) {
int j = i;
while ( j > 0 && arrays[j] < arrays[j-1]){
swap(arrays,j,j-1);
j--;
}
}
}
希尔排序
希尔排序,也是插入排序的一种,在直接插入排序的基础上,进行了序列分组,组内数字进行直接插入排序;分组需要分多次,组内数字量由少到多,排序好之后进入下一次分组。
比如说,第一次分组时,我设置增量=5,那么每隔4个数字取出一个数字,那些取出的数字组为一组,进行排序。第二次分组,我的增量=3,那么每隔2个取一个数字,取出的组为一组,进行排序。第三次增量=1,每隔0个数字取一个数字,也就是整个序列是一组,直接排序。过程如图,相同颜色为一组,组间进行直接插入排序没有给出详细过程,只给出排序后结果。
注意最后的增量必须要=1。增量自己看情况设置。
public static void shellSort(int[] arrays){
int len = arrays.length;
int d = len/2;
while(d >= 1){
insertSort(arrays,d);
d /= 2;
}
}
public static void insertSort(int[] arrays, int d){
int len = arrays.length;
for (int i = d; i < len; i++) {
int j = i;
while ( j-d >= 0 && arrays[j] < arrays[j-d]){
swap(arrays,j,j-d);
j -= d;
}
}
}
选择排序
选择排序很简单,就是每次遍历数组找到最小的数字,和前面的数字交换:
第一次遍历找到最小的数字,和第一个数字进行交换,
第二次遍历找到第二小的数字,和第二个数字进行交换,
第三次遍历找到第三小的数字,和第三个数字进行交换……
public static void choiceSort(int[] arrays){
int len = arrays.length;
for (int i = 0; i < len; i++) {
int min = i;
for (int j = i+1; j < len; j++) {
if (arrays[min] > arrays[j]) min = j;
}
swap(arrays,i,min);
}
}
堆排序
堆排序是一种完全二叉树的结构,升序是大顶堆,降序为小顶堆,小顶堆的根节点数值大小左右子节点的数值,大顶堆的根节点数值大于左右字节点的数值,也就是说小顶堆中的根节点是最小的,而大顶堆的根节点是最大的。再让大顶堆(小顶堆)的根节点不断的输出输出,就可以得到一个有序序列
大顶堆对应的数组结构为:arrays=[9,7,5,6,4,3]
堆排序的大概几个过程:(以升序排序为例)
1.首先将无序序列调整为大顶堆;
2.调整好的大顶堆的根节点同最后一个叶子节点交换,也就是把最大的数字沉到最低下;
3.根节点与叶子节点交换后,树结构被打乱,重新调整数结构,使其成为一个新的大顶堆;
4.重复2.3步骤,一直到所有节点都交换完,也就是成为了一个有序序列
画那个图实在是太难受了,代码就参考了,嘻嘻嘻,其实思路啥的也是从这儿看来的,参考地址
public static void heapSort(int[] arrays){
int len = arrays.length;
for (int i = len/2-1; i >= 0; i--) {//先调整为大顶堆 步骤一
adjust(arrays,i,len);
}
for (int i = len-1; i > 0 ; i--) {//步骤二三,交换调整的过程
swap(arrays,0,i);
adjust(arrays,0,i);
}
}
public static void adjust(int[] arrays, int i, int len){
int temp = arrays[i];
for (int j = i*2+1; j < len; j = j*2+1) {//这层循环是为了更新子树用的
if (j+1 < len && arrays[j] < arrays[j+1]){
j++;//判断是左右子节点谁比较大,那么j就指向谁
}
if(arrays[j] > temp){
arrays[i] = arrays[j];
i = j;
}else{
break;
}
}
arrays[i] = temp;
}
归并排序
归并排序,先分开,然后合并,把一个无序序列分成单个,之后两个两个的合并到一起,合并的同时进行调整,在合并的时候的调整也是小范围的调整,因为它合并时的序列是将两个有序数列进行合并。
归并有两个步骤,第一个是分开,第二个是调整合并。
分开:一个无序序列,如果要进行排序变成有序序列的话,它的前半部分要是有序的,后半部分也要是有序的,我们可以把它分开两部分,分开的两部分可以看成单独的无序序列,也分成两部分,……直到分到单个数字的时候。
合并:单个数字子序列一定是有序的,那么两个两个进行合并并调整,之后序列个数会少一半,之后再合并调整,合并调整……直到合并为一整个有序序列。
代码实现:
public static void mergeSort(int[] arrays, int left, int right){
int mid = (right + left) / 2;
if (left < right){
mergeSort(arrays,left,mid);
mergeSort(arrays,mid+1,right);
merge(arrays,left,mid,right);
}
}
public static void merge(int[] arrays, int left, int mid , int right){
int[] temp = new int[right - left +1];
int i = left;
int j = mid+1;
int k = 0;
while(i <= mid && j <= right){
if (arrays[i] < arrays[j]){
temp[k++] = arrays[i++];
}else{
temp[k++] = arrays[j++];
}
}
while( i <= mid){
temp[k++] = arrays[i++];
}
while( j <= right){
temp[k++] = arrays[j++];
}
for (int l = 0; l < temp.length; l++) {
arrays[l+left] = temp[l];
}
}
基数排序
基数排序,也说桶排序,10个桶,编号从0-9,来放数字,放数字也不能瞎放吧,它是这么来放的。嘻嘻嘻,我觉得这个排序真的很好玩儿吖,当时上课听了觉得哇好神奇。
从数字的低位到高位,即从个位到十位到百位……,先看在个位的时候,只看每个数字的个位数字,找和个位数字相同的桶序号,放进去,一个桶当然可以放很多个数字啦,也当然有可能某个桶是空的,等着数字都放完了之后,开始往外拿数字,拿也要按顺序拿,先拿0号,然后1号2号……在一个桶里有多个数字的桶里,先拿先进去的数字,就是和队差不多吖,先进去就先出来,取完数字之后,得到一个序列;
之后开始看十位,和看个位步骤一样,只不过是现在忽略掉其他位只看十位,如果是5这种没有十位的那么它的十位就是0,十位放取之后也得到一个序列,是不是这个序列越来越有序啦;那继续进行百位,同前面一样,继续千位,万位……,直到走完最高位,取出后得到一个从小到大的有序序列。
如图啦
public static void radixSort(int[] arrays){
int len = arrays.length;
// 找出最大数 并判断有几位
int max = 0;
for (int i = 0; i < len; i++) {
if (arrays[i] > max)
max = arrays[i];
}
int count = 0;
while( max / 10 != 0 ){
count++;
max /= 10;
}
count++;
//初始化10个桶子
List<List<Integer>> list = new ArrayList<List<Integer>>();
for (int i = 0; i < 10; i++) {
List<Integer> tempList = new ArrayList<Integer>();
list.add(tempList);
}
//进行取放
for (int i = 0; i < count; i++) {
for (int j = 0; j < len; j++) { // 放入桶中
int temp = arrays[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
list.get(temp).add(arrays[j]);
}
int index = 0;
for (int j = 0; j < 10; j++) { // 取出数字
for (int k = 0; k < list.get(j).size(); k++) {
arrays[index++] = list.get(j).get(k);
}
list.get(j).clear();
}
}
}