差不多开始要找工作了,因此今天特意对排序算法进行了复习,把一些心得记录下来。先给出各种算法的原理和实现,最后再做些总结吧。
1.冒泡排序,这个应该是大家都熟悉的。(都是从小到大排)
原理:简单理解就是依次把最小的数往上冒。
public void bubbleSort(int[] data) { //较小的数往前冒,每一次外层循环,保证第i个数是第i大的 for(int i=0;i<data.length;i++){ for(int j=i+1;j<data.length;j++){ //如果后面有比data[i]小的,就交换过来 if(data[i]>data[j]) swap(data, i, j); } }
其中的swap方法表示交换数组中两个位置的数据,代码如下:
private void swap(int[] data, int x, int y) { int temp = data[x]; data[x] = data[y]; data[y] = temp; }
2.选择排序
个人感觉可以看作是冒泡排序方法的改进版。冒泡排序是只要后面的比当前的小就交换,而选择排序是从后面没排好的找到最小值的位置,最后只需要交换一次。
public void selectSort(int[] data) { int index;//当前最小值的位置 for(int i=0;i<data.length;i++){ index=i; for(int j=i+1;j<data.length;j++){ if(data[index]>data[j]){ index=j;//记录最小位置 } } swap(data, i, index);//把当前最小值保存到第i个位置 } }
3.插入排序
这种方法的思路是,将一个记录插入到已排好序的有序表(有可能是空表)中,从而得到一个新的记录数增1的有序表。也就是说程序开始把第一个数当作一个有序表,然后依次加入后面的n-1个数,每加入一个数,都要保证得到的序列是排好序的。
public void insertSort(int[] data) { for (int i = 1; i < data.length; i++) { // 把第i个位置的数插到前i-1个数的某个位置,保证前i个数排好序 int temp = data[i];// 首先保存第i个数的值 int j; for (j = i; j > 0; j--) { if (data[j - 1] > temp)// 如果前面的数比第i个位置的数大,说明得往后移 data[j] = data[j - 1]; else break; } data[j] = temp;// 找到合适位置后,插入即可 } }
4.快速排序
从数组中随意找出一个数(这里取数组的第一个数),将大于和小于它的数分置于其两边,然后再将其两边的数组都以同样的方法执行 .主要分三步:1) 从数列中挑出一个元素,称为 "基准"(pivot) 2)重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割之后,这个称为分割(partition)操作。3)递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
private void quickSort(int data[], int low, int high) { int i, j, pivot; i = low; j = high; if (low < high) {// 通过这个条件结束循环 // 第一步,选择基准,这里就选择第一个数 pivot = data[i]; //第二步,通过下面这个循环,把数组中小于基准的放到数组左边,大于基准的放到数组右边 while (i < j) { // 1.从右往左找到第一个小于pivot的元素 while (i < j && data[j] > pivot) j--; if (i < j) { data[i] = data[j];// 第i个元素保存右边第一个小于pivot的 i++; } //2.从左往右找,找到第一个大于pivot的元素 while (i < j && data[i] < pivot) i++; if (i < j) { data[j] = data[i];// 第j个元素保存左边第一个大于pivot的 j--; } } data[i] = pivot;//第i个位置就是基准的位置 //第三步,递归调用该算法,把基准左边,右边的分别排序 quickSort(data, low, i - 1); quickSort(data, i + 1, high); } }
5.希尔排序(shell)缩小增量排序
Shell排序法是对相邻指定距离(称为间隔)的元素进行比较,直到使用当前间隔进行比较的元素都按顺序排序为止.Shell把间隔缩小一半,然后继续处理,当间隔最终变为1,并且不再出现变化时,Shell排序也就完成了其处理过程.实际上就是根据增量把数组分成几部分,每部分进行插入排序即可。个人理解对每个部分的排序可以采取任何的简单排序方法。
public void shellSort(int[] data) { //这里把增量初始化为数组长度的一半,然后依次减半,当然也可以传进一个增量数组 for (int inc = data.length / 2; inc > 2; inc /= 2) { for (int j = 0; j < inc; j++) { shellInsertSort(data, j, inc);//每一组内进行插入排序 } } shellInsertSort(data, 0, 1);//最后进行一次所有元素的排序 } //这个算法其实就是一次插入排序,只不过有个增量inc private void shellInsertSort(int[] data, int start, int inc) { int temp; for (int i = start+inc; i < data.length; i+=inc) { // 把第i个位置的数插到前i-1个数的某个位置,保证前i个数排好序 temp = data[i];// 首先保存第i个数的值 int j; for (j = i; j >=inc; j-=inc) { if (data[j - inc] > temp)// 如果前面的数比第i个位置的数大,说明得往后移 data[j] = data[j - inc]; else break; } data[j] = temp;// 找到合适位置后,插入即可 } }
从代码中可以看出,组内的排序方法和前面讲过的插入排序完全一样。
6.归并排序
算法思想是每次把待排序列分成两部分,分别对这两部分递归地用归并排序,完成后把这两个子部分合并成一个
序列。
归并排序借助一个全局性临时数组来方便对子序列的归并,该算法核心在于归并。
/** * @param data 是需要排序的原数组 * @param temp 是个临时数组,数组大小和data一样 * @param left 左边界 * @param right 右边界 */ private void mergeSort(int[] data, int[] temp, int left, int right) { if (left >= right)//就剩一个元素时,直接返回,直接合并就行了。 return; int mid = (left + right) / 2;//获得数组的中间位置 mergeSort(data, temp, left, mid);//对左半部分递归调用排序算法 mergeSort(data, temp, mid + 1, right);//对右半部分分递归 //这一步是把原数组中的值保存到临时数组中 for (int i = left; i <= right; i++) { temp[i] = data[i]; } //下面是完成合并,也就是把左右两个有序的数组合并到一个数组中,并使之有序 int l= left; int r = mid + 1; for (int cur = left; cur <= right; cur++) { if (l == mid + 1)//如果第一个数组已经全部比较完了,那么我们只要直接复制第二个数组的条目到合并数组中即可 data[cur] = temp[r++]; else if (r > right)//如果第二个数组已经全部比较完了,那么我们只要直接复制第一个数组的条目到合并数组中即可 data[cur] = temp[l++]; else if (temp[l] < temp[r])//把比较的两个条目中关键值小的放到合并数组中 data[cur] = temp[l++]; else data[cur] = temp[r++]; } }
7、堆排序