数据结构——排序方法总结

《剑指》里面说,面试官着重考察二分查找、快速排序和归并排序。故来总结下排序问题啦!

目录

排序问题总结

1.快速排序

(1)快排思想

(2)快排java代码

2.归并排序

(1)归并排序思想

(2)归并排序步骤

(3)归并排序java代码

3.堆排序

(1)堆排序思想

(2)堆排序步骤

(3)堆排序java代码


排序问题总结

以下重点介绍下归并、快排、堆排序,注意时间复杂度和空间复杂度的比较(都需要一定的辅助空间)

1.快速排序

(1)快排思想

通过一趟排序将数据分割成两部分——在数组中选择一个数字(通常选择第一或者最后一个),比选择数字小的移到数组左边,比选择数字大的移到右边,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程递归进行。

(2)快排java代码

	public void quickSort(int[] array,int start,int end){ 
		if(start>end){
			return; //此为递归出口,容易忘记
		}
		int i=start;
		int j=end;
		int key=array[start]; //选头部元素作为比较对象
		
		//比选择数字小的移到数组左边,比选择数字大的移到右边
		while(i<j){
			//注意扫描顺序,一定要先扫后半段
			while(array[j]>key && i<j) {j--;} //从后半段开始扫描
			while(array[i]<=key && i<j) {i++;}//从前半段开始扫描,且是小于等于

			if(i<j){
				int temp = array[i];
				array[i] = array[j];
				array[j] = temp;
			}
		}
		
		//将选择元素换到中间位置来
		int temp = array[i];
		array[i] = array[start];
		array[start] = temp;
		
		//递归
		quickSort(array,start,i-1);
		quickSort(array,i+1,end);	
	}

2.归并排序

(1)归并排序思想

采用分治法,将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

(2)归并排序步骤

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
第四步:重复步骤3直到某一指针超出序列尾,将另一序列剩下的所有元素直接复制到合并序列尾

(3)归并排序java代码

	//递归地进行划分操作
	public void mergeSort(int[] array,int start,int end){
		int mid = (start+end)/2;
		if(start<end){ //递归退出条件,如果不判断则会栈溢出
			mergeSort(array, start, mid);//左
			mergeSort(array, mid+1, end);//右
			merge(array,start,mid,end); //左右合并
		}
	}
	
	//合并有序子序列,实现升序排序(链表合并也是同理)
	public void merge(int[] array,int start,int mid,int end){
		//新申请一块合并数组空间
		int[] new_array = new int[end-start+1];
		//建立两个指针,分别指向左右序列的头部
		int i=start;
		int j=mid+1;
		int k=0; //新的合并数组的索引
		
		while(i<=mid && j<=end){
			if(array[i]<array[j]){
				new_array[k++] = array[i++];
			}
			else{
				new_array[k++] = array[j++];
			}
		}
		
		//若某个子序列仍有剩余元素,全部加入到合并数组的尾部
		while(i<=mid) {new_array[k++] = array[i++];}
		while(j<=end) {new_array[k++] = array[j++];}
		
		//将新排好序的数组放入元素相应的位置中,相当于数组的copy操作
		for(int index=0;index<new_array.length;index++){
			array[start+index] = new_array[index];
		}
	}


3.堆排序

(1)堆排序思想

堆分为最大堆和最小堆(完全二叉树),最大堆要求节点的元素都要不小于其孩子,最小堆要求节点元素都不大于其左右孩子,两者对左右孩子的大小关系不做任何要求。

我们可以得知,处于最大堆的根节点的元素一定是这个堆中的最大值。堆排序算法就是抓住了堆的这一特点,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最大堆,依次类推,最终得到排序的序列。

(2)堆排序步骤

1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区

2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn)

3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成

(3)堆排序java代码

	//堆排序,实现升序排序
    public int[] heapSort(int[] array){
        array = buildMaxHeap(array); //初始建堆,array[0]为第一趟值最大的元素
        for(int i=array.length-1;i>1;i--){  
            int temp = array[0];  //将堆顶元素array[0]和堆底元素array[i]交换,即得到当前最大元素正确的排序位置
            array[0] = array[i];
            array[i] = temp;
            adjustDownToUp(array,0,i);  //整理,将剩余的元素整理成堆
        }
        return array;
    }
    
  //构建大根堆:将array看成完全二叉树的顺序存储结构
    private int[] buildMaxHeap(int[] array){
        //从最后一个节点array.length-1的父节点(array.length-1-1)/2开始,直到根节点0,反复调整堆
        for(int i=(array.length-2)/2;i>=0;i--){ 
            adjustDownToUp(array,i,array.length);
        }
		//输出结果
        System.out.println("initial heap");
		for(int i=0; i<array.length; i++){
			System.out.print(array[i]+" ");
		}
        return array;
    }
    
    //看父节点array[k]是否比子女小,如果是,则与大的交换,array[k]下移,大的子节点上移
    //下移的array[k]只要没有到叶节点,就不断比较,判断是否需要下移(k = i的含义)
    private void adjustDownToUp(int[] array,int k,int length){
    	System.out.println("adjust "+array[k]);
        int temp = array[k];   
     
        for(int i=2*k+1; i<length; i=2*i+1){    //i初始化为节点k的左孩子,沿节点较大的子节点向下调整
            if(i<length-1 && array[i]<array[i+1]){  //i<length-1时才存在有孩子,此时比较左右孩子大小
                i++;   //如果节点的右孩子>左孩子,则取右孩子节点的下标
            }
            if(temp>=array[i]){  //根节点 >=左右子女中关键字较大者,调整结束
                break;
            }else{   //根节点 <左右子女中关键字较大者
                array[k] = array[i];  //将左右子结点中较大值array[i]调整到双亲节点上
                k = i; //【关键】修改k值,以便继续向下调整
            }
        }
        array[k] = temp;  //被调整的结点的值放入最终位置
		//输出结果

		for(int i=0; i<array.length; i++){
			System.out.print(array[i]+" ");
		}
    }
    

猜你喜欢

转载自blog.csdn.net/zhiman_zhong/article/details/88389285