快速排序(非递归)

快速排序非递归:

基本思想(默认升序):从数组中选取一个数来作为标准数,所有比这个数小的数全部放到其前面,比这个数字大的数放到其后面,此时这个标准数所处的位置就是其在有序数组中的位置,因此该标准数就不用在移动了,我们对其左右两边的数字继续执行之前的操作,这样每次都可以确定一个标准数在有序数组中的位置,直到所有数字都处在正确的位置为止,此时这个数组就为有序数组.

优化:

三数取中:我们取标准数的时候,取的数越居中则算法的时间复杂度越低越接近O(N*logN)效率越高,我们取的数越极端越接近最大或最小值则时间复杂度越高越接近O(N^2)效率越低,因此我们使用三数取中法取得的标准数更居中,具体操作是从数组的首尾中三个位置取三个数,然后以这三个数中居中的数为标准数.

扩展:

hoare法:先选取标准数,然后定义两个指针(prev(前),rear(后)),一个从前向后走,一个从后向前走,使用prev找到比标准数大的数,使用rear找到比标准数小的数,然后交换两个数,直到prev和rear走到相同位置时退出循环,再将标准数跟此处进行交换即可.

挖坑法:先选取标准数,将标数放到数组尾部然后记录到temp中,然后定义prev(前),rear(后),prev先走找到比标准数大的直接放到数组尾部,然后rear继续走找到比标准数小的直接放到prev的位置,继续重复上面的操作,直到prev和rear走到一起退出循环,将temp中的数放到此处即可.

前后指针法:先选取标准数,将标准数放到数组首部,然后定义前后指针prev在首部,cur=prev+1,两个指针一起向后走,cur找比标准数小的,prev找比标准数大的,都找到后进行交换,当cur走到末尾时退出循环,因为prev所在的位置一直是小于标准数的数中最后一位,所以将标准数与prev所在的位置进行交换,将标准数放在中间即可.

非递归思想:上面的这三种方法,都是只能针对数据做一次操作,话句话说,执行一次上面这三个算法中的一个算法只能确定出一个标准数的位置,此时就需要我们继续调用这种算法对标准数两边的数据进行操作,直到所有数据都有序时为止,使用递归调用非常方便,每次以标准数左右两侧数据为区间进行两次递归调用即可,但我们这里使用的是非递归的方法,因此我们可以使用栈来模仿递归调用时的过程,从而用循环来实现快速排序,先将数组的起始位置和终止位置入栈,然后以栈空为条件开始进行循环,取出栈中的两个元素作为区间进行一次快排调用,函数会返还一个标准值所在的位置,以其左侧的起点和终点,右侧的起点和终点进行判断在起点小于终点的情况下分别对其进行入栈,然后继续循环直到栈空为止.

图示:

扫描二维码关注公众号,回复: 13829258 查看本文章

代码:

#include<stdio.h>
#include<stdlib.h>
#define SIZE 100
 
void Swap(int* a,int* b){//对数组中的两个数据进行交换
  int temp=*b;
  *b=*a;
  *a=temp;
}
 
//取中间数
int MidNum(int* a,int b,int c,int d){
  int n_1=a[b],n_2=a[c],n_3=a[d];
  int temp[3]={n_1,n_2,n_3};
  if(temp[0]>temp[1]){
    Swap(&temp[0],&temp[1]);
  }
  if(temp[1]>temp[2]){
    Swap(&temp[1],&temp[2]);
  }
  if(temp[0]>temp[1]){
    Swap(&temp[0],&temp[1]);
  }
  if(temp[1]==a[b]){
    return b;
  }
  if(temp[1]==a[c]){
    return c;
  }
  return d;
}
 
//hoare
int PartSort1(int* a,int left,int right){//降序
  int i=left,j=right,mid=MidNum(a,left,(left+right)/2,right);//三数取中
  Swap(&a[right],&a[mid]);//将标准数放到末尾
  int temp=a[right];//记录标准数
  while(i<j){
    while(i<j && a[i]>=temp){//找比标准数小的
      i++;
    }
    while(i<j && a[j]<=temp){//找比标准数大的
      j--;
    }
    Swap(&a[i],&a[j]);//交换
  }
  Swap(&a[i],&a[right]);//将标准数放到中间
  return i;
}
 
//挖坑法
int PartSort2(int* a,int left,int right){
  int i=left,j=right,mid=MidNum(a,left,(left+right)/2,right);//三数取中
  Swap(&a[mid],&a[right]);//将标准数放到末尾
  int temp=a[right];//记录标准数
  while(i<j){
    while(i<j && a[i]<=temp){//找比标准数大的数
      i++;
    }
    if(i<j){//将其放到j的位置
      a[j]=a[i];
      j--;
    }
    while(i<j && a[j]>=temp){//找比标准数小的数
      j--;
    }
    if(i<j){//将其放到i的位置
      a[i]=a[j];
      i++;
    }
  }
  a[i]=temp;//将标准数的值放到中间
  return i;
}
 
//前后指针法
int PartSort3(int* a,int left,int right){
  int cur,prev,mid=MidNum(a,left,(left+right)/2,right);//三数取中
  Swap(&a[left],&a[mid]);//将标准数放到首部
  prev=left;//定义前后指针
  cur=prev+1;
  while(cur<=right){
    if(a[cur]<a[left] && ++prev!=cur){//cur找比标准数小的,prev找比标准数大的然后交换
      Swap(&a[cur],&a[prev]);
    }
    cur++;
  }
  Swap(&a[left],&a[prev]);//prev所在的位置永远在小于标准数的最后一位,将标准数与其交换放在中间
  return prev;
}
 
//非递归

typedef int DataType;
typedef struct Stack{//定义栈
  DataType a[SIZE];
  int size;
}Stack;

void InitStack(Stack* s){//初始化栈
  s->size=0;
}

void StackPush(Stack* s,DataType val){//入栈
  s->a[s->size++]=val;
}

void StackPop(Stack* s){//出栈
  s->size--;
}

DataType StackTop(Stack* s){//获取栈顶元素
  return s->a[s->size-1];
}

void QuickSortNoR(int* a,int left,int right){//非递归调用
  if(left>=right){//数组元素小于等于1直接退出
    return;
  }
  Stack s;//创建栈
  InitStack(&s);//初始化栈
//将数组的起点和终点入栈,因为栈是先入后出的特性,为了方便后续使用让right先入,left后入.
  StackPush(&s,right);
  StackPush(&s,left);
  while(0!=s.size){//以栈空为条件开始模仿递归调用进行循环调用.
    int i=StackTop(&s);//将数组中的两个元素取出,作为第一个区间开始调用快排.
    StackPop(&s);
    int j=StackTop(&s);
    StackPop(&s);
    int mid=PartSort3(a,i,j);//调用一次快排,并返回标准数所在的位置.
    if(mid>i+1){//判断标准数左侧是否还存在区间,存在就将其起点和终点入栈.
      StackPush(&s,mid-1);
      StackPush(&s,i);
    }
    if(mid<j-1){//判断标准数右侧是否还存在区间,存在就将其起点和终点入栈.
      StackPush(&s,j);
      StackPush(&s,mid+1);
    }
  }
}
 
void PrintArray(int* a,int n){//打印数组
  int i;
  for(i=0;i<n;i++){
    printf("%d ",a[i]);
  }
  printf("\n");
}
 
int main(){测试
  int a[]={3,4,2,7,5,6,9,8,1};
  QuickSortNoR(a,0,sizeof(a)/sizeof(a[0])-1);
  PrintArray(a,sizeof(a)/sizeof(a[0]));
  return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_49312527/article/details/123464687