当前位置 博文首页 > kbtx的博客:(王道考研笔记)快速排序、希尔排序、堆排序、归并

    kbtx的博客:(王道考研笔记)快速排序、希尔排序、堆排序、归并

    作者:[db:作者] 时间:2021-07-09 09:46

    算法代码基于王道的数据结构书修改而来,使用了面向对象的特性,代码简练,名称易懂,注释清晰,便于测试

    class QuickSort{
    
        public void sort(int[] A){
            sort(A,0,A.length-1);
        }
    
        private void sort(int[] A, int low, int high){
            if(low<high) {
                int pivot_pos = partition(A,low,high);
                sort(A,low,pivot_pos-1);
                sort(A,pivot_pos+1,high);
            }
        }
    
        private int partition(int[] A, int low, int high){
            int pivot = A[low];
            while(low<high){
                //1.1:将high递减,直到找到小于pivot的元素为止
                while(low<high&&A[high]>=pivot){--high;}
                //1.2 将low所指向的为止用这个元素填充(原有元素已经保存在pivot中,不会丢失)
                A[low] = A[high];
                //2.x 同理,low递增,直到找到大于pivot的元素为止
                while(low<high&&A[low]<=pivot){++low;}
                //覆盖原先A[high]所在位置
                A[high] = A[low];
            }
            //此时low=high
            A[low] = pivot;
            return low;
        }
    }
    
    class ShellSort{
        public void sort(int[] A){
            //步长、临时变量、组间指针、组内指针
            int step_length,tmp,inter_group_ptr,inner_group_ptr;
            int len = A.length;
            //步长按照数组长度/2向下取整,逐级递减
            for(step_length = len/2;step_length>=1;step_length/=2){
                //在分组之间来回切换,组间指针从第一个分组的第二个数开始处理
                for(inter_group_ptr=step_length; inter_group_ptr < len; inter_group_ptr++){
                    //每一个分组的前一个数都应该小于后一个,否则进行调整
                    if(A[inter_group_ptr-step_length]>A[inter_group_ptr]){
                        //将组间指针指向的数保存在临时变量中
                        tmp = A[inter_group_ptr];
                        //组内指针的初始位置是组间指针指向的数所属组的前一个数
                        inner_group_ptr = inter_group_ptr - step_length;
                        //只要下标不越界,且临时变量中的数没有放到正确的位置
                        while(inner_group_ptr>=0&&tmp<A[inner_group_ptr]){
                            //后移组内指针所指对象(A[inter_group_ptr]已经被覆盖)
                            A[inner_group_ptr+step_length]=A[inner_group_ptr];
                            //前移组内指针
                            inner_group_ptr-=step_length;
                        }
                        //将临时变量放到它在组内合理的位置
                        A[inner_group_ptr+step_length] = tmp;
                    }
                }
            }
        }
    }
    
    class HeapSort{
    
        public void sort(int[] A){
            BuildMaxHeap(A);
            int heapTop = 0;
            //注意当heapBottom = heapTop+1时,意味着只剩下两个元素需要调整了,此时再进行调整有会把两个元素中较大的重新排到前面,即前两个元素会乱序
            //当heapBottom=headTop时,循环退出
            for(int heapBottom = A.length-1; heapBottom>heapTop ;--heapBottom){
                //交换堆顶元素和堆底元素
                int tmp = A[heapBottom];
                A[heapBottom] = A[heapTop];
                A[heapTop] = tmp;
                //调整堆(此时最后一个元素已经排好序了,不参与调整
                HeadAdjust(A,heapTop,heapBottom-1);
            }
            //恢复前两个元素的次序
            A[heapTop] = A[heapTop]^A[heapTop+1];
            A[heapTop+1]=A[heapTop]^A[heapTop+1];
            A[heapTop] = A[heapTop]^A[heapTop+1];
        }
    
        public void BuildMaxHeap(int[] A){
            int len = A.length;
            //在完全二叉树中,下标不大于length/2-1的节点都有子树(下标从0开始)
            for(int curRoot=len/2-1;curRoot>=0;--curRoot){
                HeadAdjust(A,curRoot,len);
            }
        }
    
        private void HeadAdjust(int[] A,int root,int len){
            int tmp = A[root];//临时变量,用于存储当前根节点的信息
            //先进入到当前节点的左子树
            int curNode = 2*root + 1;
            //当前节点的下标没有越界,说明还在堆的范围内
            while (curNode<len){
                //curNode<len-1用于检测最后一个有子树的节点的右子树的存在性,如果它没有右子树,那么左子树的下标就是len-1,否则为len-2
                //比较左右子树
                if(curNode<len-1&&A[curNode]<A[curNode+1]){
                    //挑出较大的一个子节点
                    ++curNode;
                }
                //根节点大于两个直接相连的子树,不用继续调整
                if(tmp>=A[curNode]) break;
                else{
                    //用当前节点的值覆盖根节点
                    A[root] = A[curNode];
                    //以当前节点为根,下次循环继续调整
                    root = curNode;
                }
                //进入当前节点的左子树
                curNode = 2*curNode + 1;
            }
            //将临时变量放到它应该在的地方(root的值在循环中可能已被修改)
            A[root] = tmp;
        }
    }
    
    class MergeSort{
        public void sort(int[] A){
            sort(A,0,A.length-1);
        }
        private void merge(int[] A,int low,int mid,int high){
            //route_x:归并线路x,global_ptr:全局指针
            int route_1,route_2,global_ptr;
            int[] tmp_arr = new int[A.length];
            for(global_ptr = low; global_ptr <= high; global_ptr++){
                tmp_arr[global_ptr] = A[global_ptr];
            }
            //现在进行二路归并,第一路从low开始,直到mid为止,第二路从mid+1开始,直到high为止,从而实现在low到high范围内的归并
            for(route_1=low,route_2=mid+1,global_ptr=route_1;route_1<=mid&&route_2<=high;global_ptr++){
                if(tmp_arr[route_1]<=tmp_arr[route_2]) A[global_ptr] = tmp_arr[route_1++];
                else A[global_ptr] = tmp_arr[route_2++];
            }
            //这两个循环并不会被执行太多次,因为mid=(low+high)/2
            while(route_1<=mid) A[global_ptr++]=tmp_arr[route_1++];
            while(route_2<=high) A[global_ptr++]=tmp_arr[route_2++];
        }
        private void sort(int[] A,int low,int high){
            if(low<high){
                int mid = (low+high)/2;
                sort(A,low,mid);
                sort(A,mid+1,high);