从快速排序的递归方式和非递归方式来看函数调用帧栈

#include<iostream>

#include<vector>

#include<stack>

#include<cstdlib>

#include<algorithm>

using namespace std;

//根据轴分开两边

template <typename Data>

int partition(vector<Data> &vec,int low,int high){

   Data pivot=vec[low];  //第一个为轴

   while(low < high){

       while(low < high && vec[high]>=pivot)high--;

       vec[low]=vec[high];

       while(low < high && vec[low]<=pivot)low++;

       vec[high]=vec[low];

   }

   vec[low]=pivot;

   return low;

}

//递归快速排序

template<typename Data>

void quicksort_recurcive(vector<Data> &vec,int low,int high){

   if(low<high){

       int mid=partition(vec,low,high);

       quicksort_recurcive(vec,low,mid-1);

       quicksort_recurcive(vec,mid+1,high);

   }

}

//非递归快速排序

template<typename Data>

void quicksort_no_recurcive(vector<Data> &vec,int low,int high){

   stack<pair<int,int>> st;

   int mid;

   if(low<high){

       st.push(make_pair(low,high));

       //栈用来保存区间

       while(!st.empty()){

           low = st.top().first;

           high = st.top().second;

           st.pop();

           mid=partition(vec,low,high);

           if(low < mid - 1){

               st.push(make_pair(low,mid-1));

           }

           if(mid + 1 < high){

               st.push(make_pair(mid+1,high));

           }      

       }

   }

}

int main(){

   int len=1000000;// 100w 1000000

   vector<int> vec;

   for(int i=0;i<len;i++) vec.push_back(rand());

   

   {

       clock_t t1=clock();

       quicksort_recurcive(vec,0,len-1);

       clock_t t2=clock();

       cout<<"quicksort_recurcive  "<<double(t2-t1)/CLOCKS_PER_SEC<<endl;

       //for (auto i:vec) cout<<" "<< i<<endl;

   }

   {

       random_shuffle(vec.begin(),vec.end());//重新打乱顺序

       

       clock_t t1=clock();

       quicksort_no_recurcive(vec,0,len-1);

       clock_t t2=clock();

       cout<<"quicksort_no_recurcive  "<<double(t2-t1)/CLOCKS_PER_SEC<<endl;

       //for (auto i:vec) cout<<" "<< i<<endl;

   }

   

   return 0;

}

/*

quicksort_recurcive  0.46

quicksort_no_recurcive  0.55

*/

可以看出的是非递归使用的时间比递归的还长一些。

递归的消耗主要在于函数的调用栈。如果函数调用帧栈的消耗 不及用户自己创建的栈的,则递归方式使用的时间会更短。

以下分析的是函数调用帧栈的使用

下图是函数调用压栈的变量和顺序

 

从快速排序的递归方式和非递归方式来看函数调用帧栈

 

ESP是栈顶指针, EBP是栈底指针, 根据两个的偏移量就可以指定栈当中的内容。

栈理解为函数调用开辟的一个空间,但是该空间可以在用完后销毁。

空间保存的变量包括:

主调函数的实参N~1→主调函数返回地址(EIP,下一条要执行的命令的代码的地址)→主调函数帧基指针EBP→被调函数局部变量1~N  ->下个函数的实参(如果有)

实参是从右往左压入栈,而局部变量是由创建的顺序依次压入栈的。

另外, 静态局部变量是在编译时就保存在数据区的,不是在栈中。

若当前函数调用另一个函数,新调用函数有关的内容会从当前ESP所指向位置开始压栈,压栈完成后,ESP会指向新函数栈帧的栈顶。

EBP为前帧栈的开始地址。

对于快排函数quicksort_recurcive,编译器对其上一个函数的EBP寄存器、局部变量和实参和返回地址、当前函数的ebp寄存器、局部变量和返回地址,对这些压栈的消耗,相比较快排函数quicksort_no_recurcive中的栈stack的压入和弹出前后下标的消耗,前者没有后者的消耗大。

所以递归的快排就更快了。

 

quicksort_recurcive中前一个函数和当前没有局部变量,有调用函数寄存器ebp、eip,和实参vec、low、high,和被调函数ebp、eip,被调函数的对下个被调函数的实参。

quicksort_no_recurcive使用了栈stack,其默认构造为deque的适配器

template<typename _Tp, typename _Sequence = deque<_Tp> >

deque双向开口可进可出的容器

从快速排序的递归方式和非递归方式来看函数调用帧栈

我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去

deque却可以,它创造了内存连续的假象.

其实deque由一段一段构成 ,他是分段连续,而不是内存连续 当走向段的尾端时候自动跳到下一段 所以支持迭代器++ 操作,自动跳到下一段的方法由operator++实现

deque每次扩充 申请一个段

 

从快速排序的递归方式和非递归方式来看函数调用帧栈

其压栈和弹栈需要修改map和连续内存中的后面的指针。

测试过使用vector做呢用户的栈,结果是一样的。可以证明编译器操作帧栈,在操作数量级相似的情况下,效率会高于stl的容器或者适配器。