归并排序的递归和非递归(栈模拟递归,变换步长)

归并排序

归并排序大体思路就是:将排序数组左半边排序,右半边排序,在将两部分进行merge的过程。其中左半边的排序,和右半边的排序和整体排序的套路一致,因此子过程递归,就可以完成

merge的过程

merge 的过程就是两个分别有序的数组,合成一个数组,合并的过程中,谁的元素小就把谁放到前面。不过这里不申请额外空间,会比较复杂。标准做法都是申请一个和原来数据等长的数组

void merge(vector<int> & p,int left,int mid,int right) {
      vector<int> help(right - left + 1);
      int i = 0;
      int p1 = left;
      int p2 = mid+1;
      while(p1 <= mid && p2 <=right) {
          help[i++] = p[p1] <= p[p2] ? p[p1++]:p[p2++];
	}
	 while (p1 < mid) {
	     help[i++] = p[p1++];
	 }
     while (p2 < right) {
	     help[i++] = p[p2++];
	 }
	
	 for(int j = 0;j<help.size();j++) {
		p[left+j] = help[j]
	}
}

由此完成merge的过程

递归版的归并排序的实现

void mergeSort(vector<int> & p,int left,int right) {
	if (left == right) {
		return;
	}
	int mid = left + (right-left) >> 1;
	mergeSort(p,left,mid);
	mergeSrot(p,mid+1,right);
	merge(p,left,mid,right)
}

归并排序子过程结束两种情况

  • left == right
  • merge后
    子过程结束,弹栈

归并排序非递归 – 手写栈实现

上述递归排序过程,并不是以子过程结束而结束,子过程(左子过程,右子过程)结束,并且merge完成之后才结束。因此手写的栈里面,除了写入左边界和右边界信息外,还需要写 左子过程的状态和右子过程的状态

void changePop(vector<int> & next) {
   // 子过程结束(左子过程先结束,右子过程后结束),修改栈顶元素,
   //  next[2] 左子过程结束标识  next[3] 右子过程结束标识
    if (next[2]) {
        next[3] = true;
    } else {
        next[2] = true;
    }
}
/**
     * 大体思路 :
     * 归并排序子过程有两种情况结束:
     *    1)左边界和右边界相等
     *    2)merge完之后,
     * 子过程结束,弹栈。手动实现的话,还需要标识出是左半部分子过程还是右半部分子过程
     * 参照归并排序的递归版实现,设计栈存储的元素为:左边界,右边界,左半部分子过程结束标志 右半部分子过程结束标志,
     * 伪代码:
     * if l == r:
     *    弹栈;
     *    修改新的栈顶元素(左有序标识变为true,如果左已经为true,修改右有序标识为true);
     *    continue;
     * member = stack.top()
     * if !member.leftFlag:
     *     // 左半部分无序
     *     stack.push(l,l+(r-l)>>2,false,false);
     *     continue;
     * if !member.rightFlag():
     *     // 左半部分有序,右半部分无序
     *     stack.push(l,l+(r-l)>>2,false,false);
     *     continue;
     * // 左半部分有序 右半部分有序
     * merge(l,l+(r-l)>>2,r);
     * 弹栈
     * 修改新的栈顶元素(左有序标识变为true,如果左已经为true,修改右有序标识为true;
     */
void mergeSort2(vector<int> &p, int r) {
    
    if (r <= 1) {
        return;
    }
    stack<vector<int>> stack;
    // 栈里面存储 左边界 右边界 左半部分是否完成 右半部分是否完成
    stack.push({0, r, false, false});
    while (!stack.empty()) {
        vector<int> & member = stack.top();
        // 如果只有一个元素,弹出栈,并修改新的栈顶元素的顺序标识,归并排序都是先排序左半部分,在排序右半部分,
        if (member[0] == member[1]) {
            // 子过程结束情况1
            stack.pop();
            changePop(stack.top());
            continue;
        }
        if (!member[2]) {
            // 左半部分无序
            stack.push({member[0], member[0] + (member[1] - member[0]) / 2, false, false});
            continue;
        }

        if (!member[3]) {
            // 左有序 右无序
            stack.push({member[0] + 1 + (member[1] - member[0]) / 2, member[1], false, false});
            continue;
        }

        // 左右都有序 子过程结束情况2
        merge(p, member[0], member[0] + (member[1] - member[0]) / 2, member[1]);
        stack.pop();
        if (!stack.empty()) {
            changePop(stack.top());
        }
    }
}

由此就完成了一个非尾递归用栈操作的方式改成 非递归的形式

归并排序非递归 – 修改步长

 /** mergeSort
 * 非递归版:
 * 一步是一个组,两个组之间merge
 * 二步是一个组,两个组之间merge
 * 四步是一个组,两个组之间merge
 * 八步一个组,两个组之间merge
 * 以此类推
 * 
 **/
void mergeSort(vector<int> p) {
	// 如果这里的边界为 i <= p.size(),也可以,只是会对一次merge的过程
    for(int i=1;i<p.size();i*=2){
       // 这个循环跳出前,i的值肯定>= p.size() / 2
        // 循环的边界条件  j+i-1 < p.size() 是要合并的两个组,第一个组的最后一个元素可以取到
		for(int j=0;j+i-1 < p.size();j+=i*2) {
			int mid = j+i-1;
			int right = j+2*i-1 < p.size() ? j+2*i-1:p.size() -1;
			merge(p,j,mid,right);
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值