选择排序
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
public class SelectTest { public static void main(String[] args) { //{1,2,3,4,5}有序数组 int arrays[]={9,6,2,3,5};//任意一个无序数组 //int arrays[]={1,2,3,4,5}; select_sort(arrays); for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i]+" "); } } //实现选择排序的函数 /** * 选择要注意的特点:整体比较的次数为数组的长度-1 * 原因是最后的元素无需比较,就已经是最大的元素值了 * @param arrays */ public static void select_sort(int arrays[]){ //整体的比较次数 for (int i = 0; i < arrays.length-1; i++) { //找一个最小的元素下标 int minIndex=i; for (int j = i+1; j < arrays.length; j++) { //如果在数组的剩余元素中 还存在着比MinIndex对应更小的元素 //将minIndex的值重新赋值 if(arrays[j]<arrays[minIndex]){ minIndex=j; } } if(minIndex!=i){//不满足此条件证明arrays[i]已经是最小的元素值了 不发生交换 System.out.println("hello"); //元素值交换的过程 int temp=arrays[i]; arrays[i]=arrays[minIndex]; arrays[minIndex]=temp; } } } }
插入排序
插入排序:以数组左边的第一个元素视为有序列表,将右侧剩余的元素进行插入操作
如果插入的第一个元素比左侧的元素值要小 那么就将该元素的值放置到最左侧
public class InsertSort { public static void main(String[] args) { //{1,2,3,4,5}有序数组 int arrays[]={9,6,2,3,5};//任意一个无序数组 //int arrays[]={1,2,3,4,5}; insert_sort(arrays); for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i]+" "); } } /** * 外层循环第一次执行 * i=1 k=0(有序列表) * temp=6 * arrays[k]=9 {9,6,2,3,5} 后移过的结果 {9,9,2,3,5} * k-- k=-1 k+1 * 1 声明临时变量 保存无序列表中的第一个元素的值 * 2 临时变量和有序列表中的元素进行对比(全部对比) 找到了要插入的位置 * 3 真正的插入操作 * * @param arrays */ private static void insert_sort(int[] arrays) { //条件需要改吗? for (int i = 1; i < arrays.length; i++) { //第一次要区分有序部分和无序部分 将数组下表为0的元素视为有效元素 //后续的元素:无序元素 int k=i-1;//当前元素的前一个元素的下标 int temp=arrays[i];//将要比较的变量放置到临时变量中 for (; k>=0&&arrays[k]>temp;k--) { //元素后移的操作 arrays[k+1]=arrays[k]; } arrays[k+1]=temp;//插入动作 } } }
冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
- 针对所有的元素重复以上的步骤,除了最后一个;
- 重复步骤1~3,直到排序完成。
function bubbleSort(arr) { var len = arr.length; for (var i = 0; i < len - 1; i++) { for (var j = 0; j < len - 1 - i; j++) { if (arr[j] > arr[j+1]) { // 相邻元素两两对比 var temp = arr[j+1]; // 元素交换 arr[j+1] = arr[j]; arr[j] = temp; } } } return arr; }
希尔排序
1959年Shell发明,第一个突破O(n)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
function shellSort(arr) { var len = arr.length; for (var gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) { // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行 for (var i = gap; i < len; i++) { var j = i; var current = arr[i]; while (j - gap >= 0 && current < arr[j - gap]) { arr[j] = arr[j - gap]; j = j - gap; } arr[j] = current; } } return arr; }
计数排序
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
1.找出待排序的数组中最大和最小的元素;
2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
public class CountSortTest { public static void main(String[] args) { // 长度为20的0~10之间的随机数组 int arrays[] = { 9, 8, 2, 3, 5, 4, 9, 7, 1, 10, 0, 6, 9, 7, 10, 8, 3, 2, 1, 4 }; countSort(arrays); for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i]+" "); } } private static void countSort(int[] arrays) { //真正实现计数排序逻辑 int len=arrays.length; //20 int max=arrays[0];//将数组中的第一个元素视为最大值 for (int i = 1; i < len; i++) { //找到数组中真正的那个最大值 if(max<arrays[i]){ max=arrays[i];//打擂思想 } } //创建一个下标为从0~10这样的数组 长度为11 int nums[]=new int[max+1]; //arrays数组中的每个元素都是nums数组的下标 //nums=[1, 2, 2, 2, 2, 1, 1, 2, 2, 3, 2] for (int i = 0; i < arrays.length; i++) { nums[arrays[i]]++;//nums[9]++ 三次 nums[9] 0==>3 } int k=0;//作为我们一会的迭代变量 i 0~10 //外层循环变量:索引 随机数组中的元素 for (int i = 0; i < nums.length; i++) { //内层循环变量:nums数组的元素 次数 for (int j = nums[i]; j >0; j--) { //k++:++是写在后面的 先操作 再加 arrays[k++]=i;//将无序数列覆盖为有序数列 } } } }
快速排序
什么是快速排序:和归并排序稍微有点类似 也是在这里要获取中间值
基于:中轴元素(保证在中轴元素左侧 所有的元素均小于该中轴元素
保证在中轴元素的右侧 所有的元素均大于该中轴元素)
public class QuickSortTest { public static void main(String[] args) { int arrays[]={1,3,5,7,2,4,6,8};//要排序的数组 quickSort(arrays,0,arrays.length-1); for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i]+" "); } } /** * @param arrays:要排序的数组 * @param left:左边界 * @param right:右边界 * 停止递归的条件:在左右边界完全相等的情况下 证明数组只剩下一个元素了 * 其必定是中轴元素 (有序) */ private static void quickSort(int[] arrays,int left,int right) { if(left<right){ //编写一个方法: 1 能够找到中轴元素放在合适的位置上 2 返回中轴元素对应的下标位置 int middle=getMiddle(arrays,left,right); //递归操作 quickSort(arrays, left, middle-1); quickSort(arrays, middle+1, right); } } private static int getMiddle(int[] arrays, int left, int right) { //1:默认选择一个元素 作为数组中的中轴元素(左边界的第一个值) int middle=arrays[left]; //2:从默认的中轴元素后的第一个元素开始寻找小于中轴元素的序列 int i=left+1; //3:从数组末尾的元素寻找比中轴元素大的元素 int j=right; // 情况:巧合(恰好选择的中轴元素是最大的)i<=j下标越界的问题 // 巧合(恰好选择的中轴元素是最小的) 中轴元素位置不动 while(true){ while(i<=j&&arrays[i]<middle) i++; while(i<=j&&arrays[j]>middle) j--; if(i>=j)break; //防止的意外情况:中轴元素后的第一个元素本身就比他大 获取比它小的元素进行位置交换 int temp=arrays[i]; arrays[i]=arrays[j]; arrays[j]=temp; } //实现元素交换 把中轴元素middle放在该放的位置上 arrays[left]=arrays[j]; arrays[j]=middle; return j; } }
归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
1.把长度为n的输入序列分成两个长度为n/2的子序列;
2.对这两个子序列分别采用归并排序;
3.将两个排序好的子序列合并成一个最终的排序序列。
public class MergeSortTest { public static void main(String[] args) { //局部数组1和局部数组2都必须是有序的 //[1]和[2] [1,2] 什么时候不能排 left=right left=0 right=1 //[1,2,3,5,7]和[4,6,8,10]=1 2 3 4 5 6 7 8 10 //int arrays[]={1,2,3,5,7,4,6,8,10};//是由两个局部数组构成的大数组 int arrays[]={7,2,3,5,6,1,10};//不是规则数组 递归 dg(arrays,new int[arrays.length],0,arrays.length-1); for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i]+" "); } } public static void dg(int arrays[],int temp[],int left,int right){ //middle的值是通过自行计算的 left=right的情况下 只剩下一个了 有序 进行归并 int middle=(left+right)/2; if(left<right){ //实现递归操作 dg(arrays,temp,left,middle); //遍历数组左侧的值 dg(arrays,temp,middle+1,right);//遍历右侧的值 //直到变成元素个数为1的数组的情况下 归并操作 mergeSort(arrays,temp,left,middle,right); } } /** * 在没有做递归的情况下 : left=0 right=length-1 0 right=middle * 注意递归问题:左右边界的开始 * @param arrays:要排序的数组 * @param temp:临时数组 存放已排序好的元素 * @param left:左边界 * @param middle:分割边界 * @param right:右边界 * @return */ private static void mergeSort(int[] arrays, int[] temp, int left, int middle, int right) { //i:局部数组1的下标起始位置 j:局部数组2的下标的起始位置 int i=left,j=middle+1; //递归情况下 要不断 以左边界的位置开始 到右边界结束 for (int k =left; k<=right; k++) { //总共会有四种情况 /** * 为什么是小于等于:稳定排序(排序后元素的位置并没有发生改变) * i++:将++操作写在后面 先赋值再累计 */ if(i>middle){//如果左边的数组的元素已取完 则拼接右侧数组剩余的元素 temp[k]=arrays[j++]; }else if(j>right){ //如果右边的数组的元素已取完 则拼接左侧数组剩余的元素 temp[k]=arrays[i++]; }else if(arrays[i]<=arrays[j]){ temp[k]=arrays[i++];//把较小的元素放置到临时数组 }else{ temp[k]=arrays[j++]; } } //temp已被排序好了 从左边距开始 一直到右边距 对原有数组元素的值进行覆盖 for (int k = left; k <=right; k++) { arrays[k]=temp[k];//把排序好的值覆盖到原数组 } } }
桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
public class BucketSortTest { public static void main(String[] args) { double arrays[] = { 4.5, 3.14, 2.56, 2.02, 0.5};// 长度为5的随机数组 bucket_sort(arrays); for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i] + " "); } } private static void bucket_sort(double[] arrays) { //声明了最大值和最小值 double max=arrays[0]; double min=arrays[0]; for (int i = 0; i < arrays.length; i++) { if(max<arrays[i]) max=arrays[i]; if(min>arrays[i]) min=arrays[i]; } //获取一下两者的差值 double sub=max-min; //声明一下桶的数量 int bucketNum=arrays.length; //创建数据结构了 二维集合 一个ArrayList 我们把ArrayList集合中的LinkedList作为每一个桶 //声明集合中元素的个数为bucketNum ArrayList<LinkedList<Double>> lists=new ArrayList<LinkedList<Double>>(bucketNum); //初始化一下 for (int i = 0; i < bucketNum; i++) { lists.add(new LinkedList<Double>()); } //向桶中放置元素 思路? for (int i = 0; i < arrays.length; i++) { //随机元素:arrays[i] //获取随机元素应该放置的下标位置 int int index=(int)((arrays[i]-min)/(sub/(bucketNum-1))); //插入操作 lists.get(index).add(arrays[i]); } //对桶进行局部排序 LinkedList Collections.sort() for (int i = 0; i < lists.size(); i++) { Collections.sort(lists.get(i));//局部排序 } int k=0; //输出回原数组 for (LinkedList<Double> linkedList : lists) { for (Double item : linkedList) {//一个桶中可能保存了多个元素值 //使用有序数列覆盖无序数列 arrays[k++]=item; } } } }
基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
1.取得数组中的最大数,并取得位数;
2.arr为原始数组,从最低位开始取每个位组成radix数组;
3.对radix进行计数排序(利用计数排序适用于小范围数的特点);
var counter = []; function radixSort(arr, maxDigit) { var mod = 10; var dev = 1; for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) { for(var j = 0; j < arr.length; j++) { var bucket = parseInt((arr[j] % mod) / dev); if(counter[bucket]==null) { counter[bucket] = []; } counter[bucket].push(arr[j]); } var pos = 0; for(var j = 0; j < counter.length; j++) { var value = null; if(counter[j]!=null) { while ((value = counter[j].shift()) != null) { arr[pos++] = value; } } } } return arr; }
堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
var len; // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量 function buildMaxHeap(arr) { // 建立大顶堆 len = arr.length; for (var i = Math.floor(len/2); i >= 0; i--) { heapify(arr, i); } } function heapify(arr, i) { // 堆调整 var left = 2 * i + 1, right = 2 * i + 2, largest = i; if (left < len && arr[left] > arr[largest]) { largest = left; } if (right < len && arr[right] > arr[largest]) { largest = right; } if (largest != i) { swap(arr, i, largest); heapify(arr, largest); } } function swap(arr, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } function heapSort(arr) { buildMaxHeap(arr); for (var i = arr.length - 1; i > 0; i--) { swap(arr, 0, i); len--; heapify(arr, 0); } return arr; }