- 2.将整个区间划分三个区间:[left,keyIndex-1] keyIndex [keyIndex+1,right],由于栈是先进后出的原则,先把右区间压栈到下面,左区间压栈到上面;
- 3.如果左半区间内有多个数据,重复步骤1和步骤2,直到左半区间全部处理完了,再去处理右区间;
- 4.如果右半区间内有多个数据,重复步骤1和步骤2,当右半区间有序后,整个序列就就是有序的。
代码
void QuickSortNonR(int\* a,int n)
{
ST st;
StackInit(&st);//初始化一个栈
StackPush(&st, n - 1);//把数组中最后一个元素入进去
StackPush(&st, 0);//把数组中第一个元素入进去
while (!StackEmpty(&st))
{
int left = StackTop(&st);//取栈顶数据
StackPop(&st);//出栈
int right = StackTop(&st);
StackPop(&st);
//单趟排选谁无所谓
int keyIndex = PartSort1(a, left, right);//这里单趟排序用的是挖坑法,前后指针和左右指针都可以。
//[left,keyIndex-1] keyIndex [keyIndex+1,right]此时数组被分为了三个区间
//入栈先入右区间,再入左区间,这样就可以先处理左区间,再处理右区间
if (keyIndex + 1 < right)//表示右区间内还有多个数据,无序就入栈
{
StackPush(&st, right);
StackPush(&st, keyIndex + 1);
}
if (left < keyIndex - 1)//表示左区间内还有多个数据,无序就入栈
{
StackPush(&st, keyIndex - 1);
StackPush(&st, left);
}
}
StackDestory(&st);
}
void TestQuickSort()
{
int a[] = { 49, 38, 65, 97, 76, 13, 27, 49 };
QuickSortNonR(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
快排非递归总结
快排非递归思想性能和递归思想两者性能几乎没有差距,但是非递归很好的解决了栈溢出问题。
归并排序非递归
思想
在上文中我们了解到递归改非递归有两种,一种是循环解决,另一种是用栈模拟递归😊😊😊
**这里我们使用循环就完全可以了。**归并排序递归思想和非递归思想恰恰相反,递归思想是将序列分解为不可再分的子序列再进行归并,而非递归是将序列直接以不可再分的子序列进行归并。如图:
代码
void MergeSortNonR(int\* a, int n)
{
int\* tmp = (int\*)malloc(sizeof(int) \* n);
int gap = 1;//每组数据个数
while (gap < n)
{
for (int i = 0; i < n; i += 2 \* gap)
{
//[i,i+gap-1] [i+gap,i+2\*gap-1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 \* gap - 1;
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//拷贝回去--把临时数组内的元素拷贝到原数组中
}
for (int j = 0; j < n; ++j)
{
a[j] = tmp[j];
}
gap \*= 2;//刚才是11一组归并,\*=2让2倍归并
}
free(tmp);
}
void TestMergeSort()
{
int a[] = { 10, 6, 7, 1, 3, 9, 4, 2 };
MergeSortNonR(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{
printf("归并排序-非递归:");
TestMergeSort();
return 0;
}
**问题:**😧😧😧
上面是我们理想的状态,就像满二叉树一样,数组长度不是2的整数次方的话,就会存在数组越界的情况,总结一下有以下三种情况:
- 1.归并过程中左区间存在,但是右半区间不存在;
- 2.归并过程中左区间存在,但是右半区间有且只有一部分;
- 3.归并过程中左半区间有且只有一部分,右半区间不存在。
**问题处理:**😖😖😖
对上面三个问题的解决:
- 1.那就不需要归并了
if (begin2 >= n) break;
- 2.将end2修正一下
if (end2 >= n) { end2 = n - 1; }
- 3.不对左半区间进行处理,将拷贝回去的代码放到for循环里,归并一部分拷贝一部分
for (int j = i; j <= end2; ++j) { a[j] = tmp[j]; }
//归并排序——非递归
//时间复杂度是O(N\*logN)
void MergeSortNonR(int\* a, int n)
{
int\* tmp = (int\*)malloc(sizeof(int)\*n);
int gap = 1;//每组数据个数
while (gap < n)
{
for (int i = 0; i < n; i += 2\*gap)
{
//[i,i+gap-1] [i+gap,i+2\*gap-1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 \* gap - 1;
//归并过程中右半区间可能不存在
if (begin2 >= n)
break;
//归并过程中右半区间有且不多(算多了)修正一下
if (end2 >= n)
{
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//拷贝回去--把临时数组内的元素拷贝到原数组中
for (int j = i; j <= end2; ++j)
{
a[j] = tmp[j];
}
}
gap \*= 2;//刚才是11一组归并,\*=2让2倍归并
}
free(tmp);
}
void TestMergeSort()
{
int a[] = { 10, 6, 7, 1, 3, 9, 4, 2 };
MergeSortNonR(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
归并非递归总结
时间复杂度还是不错的和归并排序递归是一样的都是O(N*logN)。
总代码
Stack.h
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
typedef int STDataType;
typedef struct Stack
{
STDataType\* a;//是一个指针指向这个数组
int top;//栈顶
int capacity;//容量
}ST;
//栈需要的接口
// 初始化栈
void StackInit(ST\* ps);
// 销毁栈
void StackDestory(ST\* ps);
//入栈
void StackPush(ST\* ps, STDataType x);
//出栈
void StackPop(ST\* ps);
//取栈顶的数据
STDataType StackTop(ST\* ps);
//栈的数据个数
int StackSize(ST\* ps);
//检测栈是否为空,如果为空返回非零结果,如果非空返回0
bool StackEmpty(ST\* ps);//用布尔来判断真假更好用,但是注意引用头文件
Stack.c
#include"Stack.h"
void StackInit(ST\* ps)
{
assert(ps);
ps->a = (STDataType\*)malloc(sizeof(STDataType) \* 4);
if (ps->a == NULL)
{
printf("malloc fail\n");
exit(-1);
}
ps->capacity = 4;
ps->top = 0;
}
void StackDestory(ST\* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void StackPush(ST\* ps, STDataType x)
{
assert(ps);
//满了怎么办?---增容
if (ps->top == ps->capacity)
{
STDataType\* tmp = (STDataType\*)realloc(ps->a, ps->capacity \* 2 \* sizeof(STDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
ps->a = tmp;
ps->capacity \*= 2;//乘等2才会变成它的二倍
}
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST\* ps)
{
assert(ps);
//栈空了,调用pop,直接中止程序并报错
assert(ps->top > 0);
ps->top--;
}
STDataType StackTop(ST\* ps)
{
assert(ps);
//栈空了,调用top,直接中止程序并报错
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
int StackSize(ST\* ps)
{
assert(ps);
return ps->top;//top所在数组的下标就是栈的长度
}
bool StackEmpty(ST\* ps)
{
assert(ps);
return ps->top == 0;//如果为空返回非零结果,如果不为空返回0
}
Sort.c
这里因为快速排序非递归中用到了单趟排序,所以直接在拷贝了一份挖坑法的单趟排序,不理解的可以去看《八大排序》(下)
#include"Stack.h"
void PrintArray(int\* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
![img](https://img-blog.csdnimg.cn/img_convert/d1e58f4af46088b88a76c5d1643fc005.png)
![img](https://img-blog.csdnimg.cn/img_convert/bb9bf8cc5fd5979a3ba7f04e27425dfc.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
(int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
[外链图片转存中...(img-146Djjir-1714791537014)]
[外链图片转存中...(img-D0Ll0OVx-1714791537014)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**