Quicksort (non-recursive)

Quicksort is non-recursive:

The basic idea (ascending by default): select a number from the array as the standard number, put all the numbers smaller than this number in front of it, and put the numbers larger than this number behind it. The position is its position in the sorted array, so the standard number does not need to be moved. We continue to perform the previous operations on the numbers on the left and right sides, so that the position of a standard number in the sorted array can be determined each time. , until all numbers are in the correct positions, at which point the array is an ordered array.

optimization:

The middle of three numbers: when we take the standard number, the more central the number is, the lower the time complexity of the algorithm and the closer to O(N*logN) the higher the efficiency. The more extreme the number we take, the closer to the maximum or minimum value. The higher the time complexity, the closer to O(N^2), the lower the efficiency. Therefore, the standard number obtained by using the three-number method is more centered. The specific operation is to take three numbers from three positions at the beginning and end of the array, and then use The middle number among these three numbers is the standard number.

extension:

Hoare method : first select the standard number, then define two pointers (prev (front), rear (rear)), one from front to back, one from back to front, use prev to find a number larger than the standard number, use rear Find a number smaller than the standard number, and then swap the two numbers until the prev and rear go to the same position to exit the loop, and then swap the standard number with here.

Digging method: first select the standard number, put the scalar number at the end of the array and then record it in temp, then define prev (front), rear (rear), prev go first to find the number larger than the standard number and put it directly at the end of the array, then Rear continues to walk to find a position smaller than the standard number and put it directly in prev, and continue to repeat the above operation until prev and rear come together to exit the loop, and put the number in temp here.

Front and back pointer method: first select the standard number, put the standard number in the head of the array, and then define the front and rear pointer prev in the head, cur=prev+1, the two pointers go backward together, cur finds a smaller number than the standard number, and prev finds the ratio If the standard number is large, it is found and exchanged. When cur reaches the end, the loop is exited. Because the position of prev is always the last digit of the number smaller than the standard number, the standard number and the position of prev are exchanged, and the The standard number can be placed in the middle.

Non-recursive thinking: The above three methods can only perform one operation on the data. In other words, executing one of the above three algorithms can only determine the position of a standard number. At this time, it is necessary to We continue to call this algorithm to operate on the data on both sides of the standard number until all the data are in order. It is very convenient to use recursive calls. Each time the data on the left and right sides of the standard number is used as the interval to make two recursive calls, but We are using a non-recursive method here, so we can use the stack to simulate the process of recursive calls, so as to use a loop to implement quick sort, first push the start and end positions of the array into the stack, and then start with the stack empty as a condition Carry out the loop, take out two elements in the stack as an interval and make a quick sort call, the function will return the position of a standard value, and judge with the starting point and end point on the left side and the starting point and end point on the right side. The starting point is smaller than the ending point. In each case, push them to the stack respectively, and then continue to loop until the stack is empty.

Graphic:

 

Code:

#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;
}

Guess you like

Origin blog.csdn.net/weixin_49312527/article/details/123464687