1)插入排序(稳定)。从第二个元素开始,依次从剩余元素中选择一个,插入到前面有序的子序列中。在元素数量较少时,比较高效。
public static void insertSort(int[] nums){ if ( nums == null ){ return; } int len = nums.length; int tmp, j; for(int i = 1; i < len; ++i){ tmp = nums[i]; j = i - 1; while( j >= 0 && tmp < nums[j]){ nums[j + 1] = nums[j]; j--; } nums[j + 1] = tmp; } }
2)快速排序(不稳定,平均时间复杂度O(nlogn))
public void quickSort(int[] a, int low, int high) { if (low < high) { int location = partition(a,low,high); quickSort(a, low, location - 1); quickSort(a, location + 1, high); } } public int partition(int[] a, int left, int right) { if (left < right) { int pivot = a[left]; while(left < right){ while (left < right && a[right] >= pivot) { right--; } a[left] = a[right]; while (left < right && a[left] <= pivot) { left++; } a[right] = a[left]; } a[left] = pivot; } return left; }
3)归并排序(稳定,时间复杂度O(nlogn))
public void mergeSort(int[] a, int low, int high) { if (low < high) { int mid = (low + high) / 2; mergeSort(a, low, mid); mergeSort(a, mid + 1, high); merge(a, low, mid, high); } } public void merge(int[] a, int left, int mid, int right) { if (left < right){ int len = right - left + 1; int[] b = new int[len]; int i = left, j = mid + 1, k = 0; while(i <= mid && j <= right){ if (a[i] <= a[j]) { b[k++] = a[i++]; } else { b[k++] = a[j++]; } } while (i <= mid) { b[k++] = a[i++]; } while (j <= right) { b[k++] = a[j++]; } for (int l = 0; l < k; ++l){ a[l + left] = b[l]; } } }
4)堆排序(不稳定,时间复杂度O(nlogn))
/** * 堆排序 * @param a */ public void heapSort(int[] a) { if (a == null || a.length == 0) { return; } int len = a.length; for (int i = 0; i < len; ++i){ //createMaxHeap(a,len - 1 - i); //从小到大排序 createMinHeap(a, len - 1 - i); // 从大到小排序 swap(a, 0, len - 1 - i); } } // 建堆以及调整堆 public void createMaxHeap(int[] a, int lastIndex) { for(int j = (lastIndex - 1) / 2; j >= 0; --j){ // 从最后一个非叶子结点开始调整 int k = j; // 调整以k结点为根的子树 while(2 * k + 1 <= lastIndex){ // 找到孩子结点中的值较大者 int biggerIndex = 2 * k + 1; // 至少有一个左孩子 if (biggerIndex < lastIndex) { // 存在右孩子 if (a[biggerIndex] < a[biggerIndex + 1]) { biggerIndex++; } } if (a[k] < a[biggerIndex]) { // 需要调整子树 swap(a, k, biggerIndex); k = biggerIndex; // 循环调整 } else { // 不需要调整该子树,break。继续从下一个非叶子结点开始调整 break; } } } } // 建堆以及调整堆 public void createMinHeap(int[] a, int lastIndex) { for(int j = (lastIndex - 1) / 2; j >= 0; --j){ // 从最后一个非叶子结点开始调整 int k = j; // 调整以k结点为根的子树 while(2 * k + 1 <= lastIndex){ // 找到孩子结点中的值较小值 int lessIndex = 2 * k + 1; // 至少有一个左孩子 if (lessIndex < lastIndex) { // 存在右孩子 if (a[lessIndex + 1] < a[lessIndex]) { lessIndex++; } } if (a[k] > a[lessIndex]) { // 需要调整子树 swap(a, k, lessIndex); k = lessIndex; // 循环调整 } else { // 不需要调整该子树,break。继续从下一个非叶子结点开始调整 break; } } } } public void swap(int[] a, int i, int j) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; }
5)计数排序、基数排序、桶排序。
上述1-4是最常见和最常用的几种排序算法。但是,在大数据量排序时,依然具有较大的时间和空间复杂度。因此,需要寻求新的排序方式。
a)若了解排序对象的已知限制情况,可以使用计数排序。比如对成绩、年龄、日期等具有上下限的数据。
b)若对大数据量求前100名、前10名、排行榜等,可以采用降维的方法,对数据进行一次扫描即可得到所求结果。
比如求前10大,可以用10个空间,放入前10个,从大到小排好序。依次遍历剩余的数据,若其比最小者大,则交换两者,对新的10个元素重新排序;若其比较小者还小,则指针继续向下遍历。
c)桶排序:将大数据划分为n个桶,对各个桶中的数据进行排序,然后再将各个桶中的有序数据汇总排序,得到最终的有序序列。
比如:对考生试卷得分排序。低于60分的一个桶,60-80一个桶,80-100一个桶。
6)Tim sort?
7)判断一个链表是否是双向循环链表?