冒泡排序
-
思想:比如要把数组从小到大排序,比较相邻的两个元素的大小,要是左边的元素比较大的话,把两个元素交换,比如先比较第一个和第二个元素,然后通过交换,第二个元素就是变为那个比较大的元素,然后比较第二个元素和第三个元素,同样把更大的元素放在右边,就这样比较到最后,那么最后那个元素一定是整个序列中最大的数,所以就排好了从小到大的序列的最大元素。然后继续,上述操作,比较相邻的两个元素(但是不用比较到最后了,因为最后那个一定是最大的,为了减少运算就比到最后第二个元素就行了),排好最后第二个元素。以此类推,排到第二个这个序列就已经是有序的了。
-
动画效果,如下
-
代码如下:
我觉得的标准冒泡排序’
package com.liudashuai; import java.util.Arrays; public class TwoDimensionArray { public static void main(String[] args) { int[] arr={545,2,45,33,4,8,1,75}; bubbleSort(arr); System.out.println(Arrays.toString(arr)); } public static void bubbleSort(int arr[]){ int temp; for (int i = arr.length-1; i >=1; i--) {//i=几,就是本轮冒泡要排好的哪个元素,就比如第一波要排好arr[arr.length-1]那个位置的元素,这个i就相当于下标 for (int j = 0; j <=i-1 ; j++) {//从哪冒泡到哪,冒泡的那个标记,这个i+1还是i-1要和下面的arr[j] > arr[j + 1]对照着看才能理解 if (arr[j] > arr[j + 1]) { //哪两个冒泡比较,这里可以看出来是比较冒泡标和冒泡标后面的那个元素,且看出来是从小到大排序的,因为这里可以看出来,要是后面那个元素比前面的元素大就交换 temp = arr[j]; arr[j] = arr[j+ 1]; arr[j+1] = temp; } } } } } //建议使用<=这样比较好理解,比如 for (int i = arr.length-1; i >=1; i--) 这里的i>=1,表示i等于arr.length-1和1这个下标的时候都是可以运行的,为什么是等于1呢?因为只要排序到下标为1的,那个下标为0的就已经排好了
(这个先排好第一个元素,正好于上面的动画相反)
package com.liudashuai; import java.util.Arrays; public class TwoDimensionArray { public static void main(String[] args) { int[] arr={545,2,45,33,4,8,1,75}; bubbleSort(arr); System.out.println(Arrays.toString(arr)); } public static void bubbleSort(int arr[]){ int temp; for (int i = 0; i <=arr.length-1-1; i++) {//为什么是 i <=arr.length-1-1呢?因为只要冒泡好最后第二个元素就行了,那个序列就有序了 for (int j = arr.length-1; j >=i+1 ; j--) { if (arr[j] > arr[j - 1]) { temp = arr[j]; arr[j] = arr[j- 1]; arr[j-1] = temp; } } } } }
-
冒泡排序是稳定的,即如果比较的两个数的值是相同的,那么排序前和排序后的相对位置是不变的。比如1 2 3 2这个序列,你第一轮冒泡,把1冒到最后即2321,然后你第二轮冒泡,把第二个2冒到第二个位置,即3221,第三轮冒泡不变。排序完毕后,第一个2还是在第二个2的前面,排序前第一个2也在第二个2前面,所以,相对位置不变,所以是稳定的。
-
排序的稳定性定义:如果对于数组中出现的任意a[i],aj,其中a[i]==a[j],在进行排序以后a[i]一定出现在a[j]之前,则认为该排序是稳定的。
-
冒泡排序的优化
选择排序
-
选择排序的思想:
先把第一个当作待排序数,即先把第一个数当作为最小(大)数,并用一个标志记下他的下标,然后向后遍历,找到比标志位更小的数便记下那个更小的数的下标,然后继续向后遍历,要是比这个新下标还下的数,就又用这个标记记下新下标,一轮循环结束,那个下标就是最小数的小标了,然后把这个待排序数(即第一个数)与这个标记的数交换,这样第一个数就排序好了。下面把第二个数当作待排序数,然后用标记即下他的下标,然后向后遍历,找到更小的数,更新标记……然后与第二个数交换,……
-
动画演示
-
代码实现
package com.liudashuai; import java.util.Arrays; public class TwoDimensionArray { public static void main(String[] args) { int arr[] = {9, 5, 2,1, 7, 5,4,10,4756}; selectionSort(arr); System.out.println(Arrays.toString(arr)); } public static void selectionSort(int[] nums){ int length=nums.length; //最小值的索引 int index; for(int i=0;i<=length-1;i++){ index=i; for(int j=i+1;j<=length-1;j++){ //寻找最小的值 if(nums[j]<nums[index]){ index=j; } } int tem=nums[i]; nums[i]=nums[index]; nums[index]=tem; } } }
-
不稳定,因为,比如,3 5 6 5 4,选择一个最小的与这个第一个交换,所以第一轮选择排序是没有交换,结果还是3 5 6 5 4,但是第二轮选择排序,5和4交换,这样结果就是3 4 6 5 5,所以这两个5的相对位置变了,原来前面的5现在到后面了。然后第三轮,结果是3 4 5 6 5,第四轮是3 4 5 5 6。
直接插入排序
-
插入排序的思想:这个排序的思想是我们从第二个元素开始排,你把排序光标放在第二个元素上,然后用一个东西来记住他,比如说用temp变量来记住他,且光标移到temp上,然后把这个temp和他前面的那些已排序好的序列比较,我们在已排序的序列的数里面也搞一个光标,要是这个temp小于那个已排序的光标的数(我们把这个序列排为从小到大的序列),那个已排序的那个光标的数就向后移动一格(值得注意的是已排序序列里的光标是在已排序序列里从后向前的运动的),然后那个已排序的那个光标就向前移动一个,指向前面的数,然后再把那个已排序序列的光标的数与temp这个光标数比较,要是已排序数大就向后移动一格,光标前移,要是他小于temp,就不移动了,temp就填补那个空位。然后开始排第三元素,……。
-
动画展示:
-
代码如下
package com.liudashuai; public class TwoDimensionArray { public static void main(String[] args) { int arr[] = {9, 54, 2,10, 7, 44,4,10,4756}; insertSort(arr); printSort(arr); } public static void insertSort(int[] arr) { for (int i = 1; i <= arr.length-1; i++) {//未排序的光标一直是要从1运动到最后的 int temp = arr[i], j;//temp来记住光标,这个j放在外面是因为外面要访问他,看这arr[j+1] = temp; for (j = i - 1; j >= 0; j--) { if (arr[j] > temp) { arr[j+1] = arr[j]; } else { break; } } arr[j+1] = temp; } } public static void printSort(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } }
快速排序
-
快速排序的思想:
-
从数列中挑出一个元素,称为 “基准”(pivot);
-
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
-
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
-
-
流程展示看本文件夹下的那个视频:108813206_nb2-1-80.mp3
-
代码展示
package com.liudashuai; public class TwoDimensionArray { public static void main(String[] args) { int[] arr={8,4,5,7,1,3,6};//直接复制数组 quick_sort(arr,0,arr.length-1); print(arr); } //下面这个方法相当于,用了一个基准数,去排好一次排序。并返回一个中轴值 private static int get_mid(int arr[],int left,int right){ int pivot=arr[left];//相当于把这个arr[left]的值复制一份出来到pivot中。一开始我们拿了最左边的值出来 while(left<right){//当left与right指针相遇的时候退出循环,双指针遍历结束 while(arr[right]>=pivot && left<right) right--;//right指针一开始在最右边元素的下标,当arr[right]>=pivot,right指针向左遍历。 arr[left]=arr[right];//当arr[right]<pivot的时候,把当前值arr[right]赋给已经保存了的arr[left]中,所以这个arr[right]的这个值已经有了一个副本了。把右边的那个值放到左边这个标志这里,相当于覆盖了原来的值,因为原来的值已经保存出来了,所以覆盖了没有关系。 while(arr[left]<=pivot && left<right) left++;//到left指针从左往右遍历,当arr[left]<=pivot,满足左边的值比基准数小,left指针继续向右遍历。 arr[right]=arr[left];//当arr[left]>pivot的时候,把当前值arr[left]赋给已经有副本的arr[right]值,此时arr[left]就可以被覆盖了。 } //经历了上面的循环实现了pivot为中轴,小放左,大放右的格局 arr[left]=pivot;//最后把存放在pivot值放回数组 return left;//返回中轴所在的下标位置。 } private static void quick_sort(int[] arr,int left,int right){ if(left<right){ int mid =get_mid(arr,left,right);//接收中轴所在的下标位置。 quick_sort(arr,left,mid-1);//递归地对arr[left..mid]进行快速排序,使得左子序列有序 quick_sort(arr,mid+1,right);//递归地对arr[mid+1..right]进行快速排序,使得左子序列有序 } } public static void print(int arr[])//封装函打印函数 { for(int k=0;k<arr.length;k++) { System.out.print(arr[k]+" "); } } }