冒泡排序
void bubble_sort(int *ar, int size)
{
assert(ar != NULL);
for (int i = 0; i < size - 1; i++)
{
for (int j = 0; j < size - i - 1; j++)
{
if (ar[j]>ar[j + 1])
{
swap(ar[j], ar[j + 1]);
}
}
}
}
容易出现错误的地方是for (int j = i; j < size - i - 1; j++),在这里看似没错,但是如果j是由i赋值的话每次比较大小的开始就不是第一个了,而实跟着i开始比较,这样就会少比较几个数
void bubble_sort2(int *ar, int size)
{
assert(ar != NULL);
if (size < 2)
{
return;
}
//int flag1 = 0;
//int flag2 = 0;
for (int i = 0; i < size - 1; i++)
{
bool tag = true;
for (int j = 0; j < size - i - 1; j++)
{
if (ar[j]>ar[j + 1])
{
swap(ar[j], ar[j + 1]);
/* flag2++;*/
tag = false;
}
}
/*if (flag1 != flag2)
{
flag1 = flag2;
}
else break;*/
if (tag)
{
break;
}
}
}
第二个版本的冒泡排序加了一个标识位,如果只需要移动第一位,而其他位不用动,那么加了标识位后,通过break可以跳出剩余无用循环
选择排序
void SelectSort(SeqList &seq) 选择排序
{
int n = GetSize(seq);
for(int i = 0;i<n-1;++i)
{
int minpos = i;
for(int j = i+1;j<n;++j)
{
if(seq.data[minpos] > seq.data[j])
{
minpos = j;
}
}
if(i != minpos)
{
Swap(seq.data[minpos],seq.data[i]);
}
}
}
插入排序
void InsertSort(SeqList &seq)
{
int n = GetSize(seq);
for(int i = 1;i<n;++i)
{
if(seq.data[i] < seq.data[i-1])
{
ElemType tmp = seq.data[i];
int j = i - 1;
do
{
seq.data[j+1] = seq.data[j];
--j;
}while(j >= 0 && tmp < seq.data[j]);
seq.data[j+1] = tmp;
}
}
}
void FilterDown(SeqList &seq,int start,int end)//建立最小堆,为优先级队列和排序做先觉条件
{
int i = start,j = 2*i+1;
ElemType tmp = seq.data[i];//用于最后完成建立后交换原来根节点的值
while(j <= end) //
{
if(j < end && seq.data[j] < seq.data[j+1]) ++j; // Max
if(tmp >= seq.data[j]) break;
seq.data[i] = seq.data[j];
i = j;
j = i*2+1;
}
seq.data[i] = tmp;
}
数据结构的本质是数据之间的关系,如一对一,一对多,多对一,多对多
堆排序
的规律是如某节点的下标是i
则其左孩子的下标是2i+1
其右孩子的下标是2i+2
其双亲的下标是(j-1)/2
如某个数组是:56 78 12 23 90 86 45 34
现在需要排序
排序的形式为key i
key i<=key i2+1;
key i<=key i2+2;
在建立最小堆时,我们需要从叶节点往根节点构造,因为如果不降叶节点的最小堆确定,那么在建立根节点的最小堆时无法确定,原理和关键路径的画法很相似
所以上面的堆排序的顺序是
FilterDown(ar,3,7);
FilterDown(ar,2,7);
FilterDown(ar,1,7);
FilterDown(ar,0,7);
在调用函数是需要确定下标的值,这里计算的方式是(i-1)/2
在这里的调用顺序是无法改变的,因为在建立最小堆时,必须先确定其叶节点的最小堆是确定的,否则叶节点以上的节点最小堆也无法确定
如果该数据在末尾增加了一个数
如某个数组是:56 78 12 23 90 86 45 34 5
那么原来的最小堆就不是最小堆了我们需要重新建立
这时候如果将整个二叉树进行排序那么十分浪费时间
我们可以选择将加入的数和其双亲节点比较,如果其小于双亲节点,则交换和继续和其双亲节点比较,相当于只对整体的一般的节点进行了排序,这样的效率较高
void FilterDown(elemtype*ar, int start)//在8下标插入,所以start=8,这时的ar是已近插入了第8个数
{
int i = start;
int j = (i - 1) / 2;
while (i != 0)
{
if (ar[i] < ar[j])
{
swap(ar[i], ar[j]);
i = j;
j = (i - 1) / 2;
}
break;
}
}
堆排序完后取值时,先取根节点(0下标的值),然后把末尾下标的值放在0下标的位置,然后从根节点重新建立最小堆,之后在此取根节点的值,这就是堆排序的核心
下面附上堆排序在优先级队列中的应用
void FilterDown(ElemType *ar,int start,int end)
{
int i = start,j = 2*i+1; //
ElemType tmp = ar[i];
while(j <= end)
{
if(j < end && ar[j] > ar[j+1]) j+=1;
if(tmp <= ar[j]) break;
ar[i] = ar[j];
i = j;
j = 2*i+1;
}
ar[i] = tmp;
}
void FilterUp(ElemType *ar,int start)
{
int j = start, i = (j-1)/2;
ElemType tmp = ar[j];
while(j > 0)
{
if(ar[i] <= tmp) break;
ar[j] = ar[i];
j = i;
i = (j-1)/2;
}
ar[j] = tmp;
}
下面举例优先级队列的输入和输出的例子
void FilterDown(ElemType *ar,int start,int end)
{
int i = start,j = 2*i+1; //
ElemType tmp = ar[i];
while(j <= end)
{
if(j < end && ar[j] > ar[j+1]) j+=1;
if(tmp <= ar[j]) break;
ar[i] = ar[j];
i = j;
j = 2*i+1;
}
ar[i] = tmp;
}
void FilterUp(ElemType *ar,int start)
{
int j = start, i = (j-1)/2;
ElemType tmp = ar[j];
while(j > 0)
{
if(ar[i] <= tmp) break;
ar[j] = ar[i];
j = i;
i = (j-1)/2;
}
ar[j] = tmp;
}