各種排序演算法分析與比較
阿新 • • 發佈:2019-01-08
1.直接插入排序
每一趟將一個待排序的元素作為關鍵字,按照其關鍵字的大小插入到已經排好的部分序列的適當位置上。平均時間複雜度為O(n2),空間複雜度為O(1)。
void InsertSort(int R[], int n) { if (R == nullptr || n<=0) return; int i, j; int temp; for (i = 1; i < n; ++i) { j = i - 1; temp = R[i]; while (j >= 0 && R[j] > temp) { R[j+1] = R[j]; --j; } R[j+1] = temp; } }
2.氣泡排序
平均時間複雜度為O(n2),空間複雜度為O(1)
void BubbleSort(int R[], int n) { if (R == nullptr || n <= 0) return; int i, j; int temp; int flag = 0; for (i = n; i >= 1; --i) { flag = 0;//每次重新置為0 for (j = 1; j < i; ++j) { if (R[j - 1] > R[j]) { temp = R[j]; R[j] = R[j - 1]; R[j - 1] = temp; flag = 1;//有資料交換,flag=1; } } if (flag == 0) return; } }
3.快速排序
快速排序以樞軸為中心,將序列分成兩部分。關鍵是樞軸的劃分。
時間複雜度:最好的情況下為O(nlogn),最壞為O(n2)
空間複雜度:O(log2n)
遞迴演算法:
void QuickSort(int R[], int low, int high)
{
if (low >= high)
return;
int pivot = Partition(R, low, high);
QuickSort(R, low, pivot - 1);
QuickSort(R, pivot + 1, high);
}
幾種劃分演算法:
1.從後往前掃描直到小於樞軸的位置j,將R[j]交換到i的位置;從i開始往後掃描,直到遇到大於樞軸的位置i,把R[i]交換到j的位置。
int Partition(int R[], int low, int high)
{
int pivot = R[low];
while (low < high)
{
while (low < high && R[high] >= pivot)
--high;
if (low < high)
R[low] = R[high];
while (low < high && R[low] <= pivot)
++low;
if (low < high)
R[high] = R[low];
}
R[low] = pivot;
return low;
}
2.從前往後掃描,遇到小於樞軸的值,則遞增small值,如果當前下標與small不相等,則交換,保證small左邊的元素都小於樞軸。
int Partition(int data[], int start, int end)
{
int index= RandomInRange(start,end);//隨機生成一個數作為中樞值
swap(data[index],data[end]);//最後一個值作為中樞值
int small=start-1;
for(index=start; index<end; ++index)
{
if(data[index]<data[end])
{
++small;
if(small != index)
swap(data[index], data[small]);
}
}
++small;
swap(data[small], data[end]);
return small;
}
3.兩邊同時進行掃描,並交換位置。
int Partition3(int R[], int low, int high)
{
int pivot = R[low];
int i = low + 1;
int j = high;
while (i <= j)
{
while (i <= j && R[j] >= pivot)
--j;
while (i <= j && R[i] <= pivot)
++i;
if (i < j)
{
swap(R[i], R[j]);
++i;
--j;
}
}
swap(R[low], R[j]);
return j;
}
4.堆排序
堆是一種資料結構,可以把堆看成完全二叉樹,這棵完全二叉樹滿足:任何一個非葉節點的值都不大於(或不小於)其左右孩子節點的值。若父節點大於孩子節點,則叫大頂堆,父節點小於孩子節點,則叫小頂堆。
堆排序的主要操作是將序列調整為堆。
時間複雜度:O(n log2n);空間複雜度:O(1)
void Shift(int R[], int low, int high)
{
int i = low;
int j = 2 * i + 1;
int temp = R[i];
while (j <= high)
{
if (j < high && R[j] < R[j + 1])
{
++j;
}
if (temp < R[j])
{
R[i] = R[j];
i = j;
j = 2 * i + 1;
}
else
break;
}
R[i] = temp;
}
void HeapSort(int R[], int n)
{
int i;
int temp;
for (i = n / 2 - 1; i >= 0; --i)
{
Shift(R, i,n);
}
for (i = n-1; i >= 1; --i)
{
temp = R[0];
R[0] = R[i];
R[i] = temp;
Shift(R, 0, i - 1);
}
}
4.二路歸併排序
歸併排序的時間複雜度和初始序列無關,平均情況下為O(n log2n),最好情況下為O(nlog2n),最壞情況下也為O(nlog2n)。
空間複雜度:因歸併排序需要轉存整個待排序列,因此空間複雜度為O(n)
void Merge(int R[], int tempArray[], int low, int mid, int high)
{
int i = low;
int j = mid + 1;
int k = i;
while (i <= mid && j <= high)
{
if (R[i] < R[j])
tempArray[k++] = R[i++];
else
tempArray[k++] = R[j++];
}
while (i <= mid)
tempArray[k++] = R[i++];
while (j <= high)
tempArray[k++] = R[j++];
for (int i = low; i <= high; ++i)
{
R[i] = tempArray[i];
}
}
void MergeSort(int R[],int tempArray[], int low, int high)
{
if (low < high)
{
int mid = (low + high) / 2;
MergeSort(R, tempArray, low, mid);
MergeSort(R, tempArray, mid + 1, high);
Merge(R, tempArray, low, mid, high);
}
}
void MergeSort(int R[], int n)
{
int *tempArray = new int[n];
MergeSort(R, tempArray, 0, n - 1);
delete []tempArray;
}
5.計算排序
適用於元素的取值範圍比較小的情況。
1>統計每個元素的個數
2>統計每個元素比自身小的元素個數
template <typename Type>
void CountingSort(Type array[], int low, int high, int range)//range為元素的取值範圍
{
Type *result = new Type[high - low + 1];
Type *valueCount = new Type[range + 1];
for (int i = 0; i <= range; ++i)
{
valueCount[i] = 0;
}
for (int i = low; i <= high; ++i)
{
valueCount[array[i]] += 1;
}
for (int i = 1; i <= range; ++i)
{
valueCount[i] += valueCount[i - 1];
}
for (int i = high; i >= low; --i)
{//form high to low , in order to guarantee the stable sort
result[valueCount[array[i]] - 1] = array[i];
--valueCount[array[i]];
}
int i = low;
while (i <= high)
{
array[i] = result[i - low];
++i;
}
delete[] result;
delete[] valueCount;
}
參考部落格: