几种常见的排序算法及其算法思路:
1.选择排序 O(n方)
所谓选择就是每次都选择一个最小的元素放在最前面。思路是去寻找最小元素的坐标,然后将最小坐标的元素与当前坐标的元素交换。具体的做法是假设当前循环的第一个坐标是最小元素的坐标,然后从第二个元素开始遍历(内层循环),如果该元素比最小坐标的的元素小,就把最小元素的坐标修改为该元素的坐标,最后交换选出的最小元素坐标所在的元素和当前外层循环正在处理的元素两个元素,实现代码如下:
//交换数组中的两个元素 private static void swap(int[] arr, int i, int j) { int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } //选择排序 public static void selectSort(int[] arr) { int n = arr.length; for (int i = 0; i < n; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) minIndex = j; swap(arr, minIndex, i); } } }
2.插入排序 O(n方)
插入排序的操作和排序扑克牌的操作及其相似,目标就是每个元素都选择正确的位置进行插入,使得该序列始终处于有序状态。想象以下你打扑克牌的摸牌的时候,摸第一张牌,他本身就是有序的,无需任何操作,摸第二张牌就要考虑下,较小就放前面,较大就放后面,剩下的牌插入时,从后往前看,比前面的小就交换一次,直到比前面大就说明找到了合适的位置。最后完成插入排序,可以看出插入排序有提前结束内层循环的机会,所以它在排序较为有序的序列时速度很快,以下是代码实现:
//插入排序 public static void insertSort(int[] arr) { int n = arr.length; for (int i = 1; i < n; i++) { for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) { swap(arr, j, j - 1); } } }
3.快速排序O(n*logn)
快速排序本身是一种分治算法,即把一个大问题拆成多个相同的结构的小问题,然后递归解决小问题,大问题也就得到了解决。快速排序把问题拆分成一个个小的partition操作,partition操作是选择一个参考元素,并使得参考元素左边的元素都比他小,右边的都比他大.操作的具体做法为:选择要排序序列左端点的元素v为参考元素,把序列分为左partA,和右partB两个部分,使得partA都比v小,partB都v大,用 j 这个元素记录partA的最后一个元素的坐标(初始值为左端点l),从v后面的元素向右端点遍历,如果比v小,partA容量+1(j++),并把它作为partA的最后一个元素 ,比v大就不作任何操作,最后将参考元素 v(坐标为l),与partA最后一个元素(坐标为j)两个元素交换位置完成partition操作,并返回参考元素最后所在位置(j),之后在递归对j左右两边进行快速排序,排序完成。
// 快速排序 public static void quickSort(int[] arr) { int n = arr.length; qSort(arr, 0, n - 1); } private static void qSort(int[] arr, int l, int r) { if (l >= r) return; int p = partition(arr, l, r); qSort(arr, l, p); qSort(arr, p + 1, r); } private static int partition(int[] arr, int l, int r) { int v = arr[l]; int j = l; for (int i = l + 1; i <= r; i++) { if (arr[i] < v) {j++; swap(arr, j, i); } } swap(arr, l, j); return j; }
4. 归并排序(O(n*logn))
归并排序也是一种分治算法,这里只说自下而上的归并排序的实现,归并排序的基本思想是采用二分的方式对序列进行分割,当分到每一小块都只有一个元素时,每个片段就都是有序的,然后自下而上进行merge(归并操作),所以,归并排序的核心也是这个merge操作,归并操作需要借助一个辅助的空间来协助我们排序,首先归并时左右两个半边都须是有序的,具体思路如下:开辟一个和原要归并序列相同的辅助序列,然后对原序列的每个坐标(用k表示)进行选举操作,即从辅助序列中选举出合适的元素放在该坐标,直至遍历完原序列的每一个坐标。用 i 这个元素标记辅助序列左半边正在处理元素的坐标,用 j 这个元素标记辅助序列右半边正在操作的坐标,然后将i所在的元素与j所在的元素进行比较,谁更小就作为被选中的目标放在原序列正在处理的坐标上,并且该半边正在操作的元素的坐标后移一位,即(i++或j++),当然如果某半边操作完成,原序列选举时没的可选,就把剩余半边未操作的元素依次放在原序列正在选举的坐标上,完成该序列的归并操作,最终通过递归的方式完成归并排序,以下是代码实现:
//归并排序 public static void mergeSort(int[] arr) { int n = arr.length; mSort(arr, 0, n - 1); } private static void mSort(int[] arr, int l, int r) { if (l >= r) return; int mid = (l + r) / 2; mSort(arr, l, mid); mSort(arr, mid + 1, r); merge(arr, l, mid, r); } private static void merge(int[] arr, int l, int mid, int r) { int temp[] = Arrays.copyOfRange(arr, l, r + 1); int i = l, j = mid + 1; for (int k = l; k <= r; k++) { if (i > mid) { arr[k] = temp[j - l]; j++; } else if (j > r) { arr[k] = temp[i - l]; i++; } else if (temp[i - l] < temp[j - l]) { arr[k] = temp[i - l]; i++; } else { arr[k] = temp[j - l]; j++; } } }
5.堆排序
堆排序就是借助堆(heap)这种特殊的数据结构进行排序。堆是一颗完全的二叉树(即除了最后一层,每一层节点的个数都是饱和的,且最后一层即使不饱和,节点也都集中在左侧),可以通过实现一个最大堆来实现堆排序,最大堆(所有父节点都大于它的子节点),下面是使用数组的方式实现一个最大堆,从数组坐标为的1的位置开始存放元素,这样,每个元素左孩子坐标为父节点坐标的两倍,右孩子是父节点坐标的两倍+1, 入堆和出堆操作时使数组始终保持最大堆的特性,核心是两个shift操作。下面是最大堆代码实现 。
public class MaxHeap<Item extends Comparable> { protected Item[] data; protected int count;//当前元素的个数 protected int capcity;//最大堆的容量 public MaxHeap(int capcity){ data= (Item[]) new Comparable[capcity+1]; count=0; this.capcity=capcity; } public MaxHeap(Item[] items){ int n=items.length; capcity=n; data= (Item[]) new Comparable[n+1]; count=n; for(int i=1;i<=n;i++) data[i]=items[i-1]; for(int i=n/2;i>=1;i--) shifdown(i); } // 向最大堆中插入一个新的元素 item public void insert(Item item){ assert count+1<=capcity; data[count+1]=item; count++; shiftup(count); } // 向最大堆中取出入一个新的元素 item public Item extactTop(){ assert count>0; Item item=data[1]; swap( 1 , count ); count--; shifdown(1); return item; } private void swap(int i,int j){ Item t=data[i]; data[i]=data[j]; data[j]=t; } private void shiftup(int k) { while(k>1 && data[k/2].compareTo(data[k])<0){ swap(k/2,k); k =k/2; } } private void shifdown(int k) { while (k*2<=count){ int j=k*2; if(j+1<=count && data[j].compareTo(data[j+1])<0) j++; if(data[k].compareTo(data[j])>0) break; swap(k,j); k=j; } } public static void main(String[] args) { // MaxHeap maxHeap=new MaxHeap<Integer>(100); // for(int i=1;i<100;i=i+2){ // maxHeap.insert(i); // } // // for(int i=1;i<100;i=i+2){ // System.out.print(maxHeap.extactTop()+ " "); // } Integer[] arr=new Integer[]{1,4,9,7,5,3,1}; MaxHeap maxHeap=new MaxHeap<Integer>(arr); for (int i=0;i<arr.length;i++){ System.out.print(maxHeap.extactTop()+" "); } } }