新快排的核心思想有四點:
1. 基於快排的二分對比;
2. 二分對比的同時用最小的代價讓數組趨於有序,這樣中間值大概率位於中間位置上,二分對比取中間值作基準值效率是最優的;
3. 二分對比的同時用最小的代價檢查右邊是否有逆序,如果右邊非逆序則可跳過右邊的判斷;
4. 用循環取代遞歸調用;
新快排的步驟:
1. 設置左指針i、右指針j、中位指針p;
2. 取中位指針指向的元素爲基準數s;
3. 判斷左指針指向的元素是否小於基準數,小於,則循環判斷下一項是否比當前項小,是則交換,不是則循環第3步;
4. 判斷左指針指向的元素是否大於基準數,大於,則交換左指針i與中位指針p所指的元素;
5 判斷右指針指向的元素是否大於基準數,大於,則循環判斷下一項是否比當前項大,是則交換,不是則循環第5步;
6. 判斷右指針指向的元素是否小於基準數,小於,則交換右指針j與左指針i所指的元素;
7. 循環1至7步,直到i>j;
代碼如下( 注:如果彙編級優化,效率還可大幅提高)
#include "stdafx.h"
#include <algorithm>
#include <ctime>
using namespace std;
#define MAX_SIZE 10000
#define TEST_TIMES 1000
#define vksize 1000
// 新快速排序算法(新快排)
// 參數: 區間表起始,區間表結尾; 區間表格式: 起始位置1,結束位置1,起始位置2,結束位置2 ....
void xsort(int **r, int **x) {
static int *i, *j, *p; // 左指針,右指針,軸項指針
static int **k = x; int **o = r; // 起始暫存,結尾暫存
static int s; // 關鍵字
static bool b; // 標記軸右邊是否有交換位置;
while (r >= o) {
i = *r;
j = *(r + 1);
while (i < j) { // >=2 個元素
p = j - ((j - i) >> 1); // 取中間值爲軸項(偏右邊)
s = *p;
b = false;
do {
// 要點:
// 1. 關鍵字s始終在 *i與*j之間,起到擋板作用,免去i,j比較的同時,也可防止i\j過頭
// 2. 相鄰兩項判斷,逆序就反轉;
// 3. 如果右邊沒有一次交換,則右邊爲有序,b作爲標記;
while (*i < s) { register int vs = *i; i++; while (vs > *i) { *(i - 1) = *i; i++; }; if (*(i - 1) != vs) *(i - 1) = vs; }
// *i>=s
if (*i > s) { *p = *i; *i = s; b = true; }
// *i==s
while (*j > s) { register int vs = *j; j--; while (vs < *j) { *(j + 1) = *j; j--; }; if (*(j + 1) != vs) { *(j + 1) = vs; b = true; } }
// *i==s *j<=s
if (*j < s) { *i = *j; *j = s; b = true; }
// *i>=s
p = j;
++i;
} while (i < j);
while (--j > *r && *j == s);
if (b) {
i = p;
while (++i < *(r + 1) && *i == s);
if (i < *(r + 1)) { // 右邊執行
if (j > *r) { // 左邊暫存
// 區間暫存不夠用時,把空間壓縮一半;
if ((r + 3) > k) { // x>=o + 3
if (k < x) { // 有折半
*o = *k;
*k = *x;
} else { // 沒折半
k = x - ((x + 1 - o) >> 1);
*k = *o;
*(o + 1) = *(r + 1);
*(r + 1) = j;
r = o;
*r = i;
j = *(r + 1);
continue; // 下一次循環;
}
}
*(r + 3) = *(r + 1);
*(r + 1) = j;
r++; r++;
}
*r = i;
j = *(r + 1);
continue; // 下一次循環;
}
}
if (j > *r) {
*(r + 1) = j;
i = *r;
continue; // 下一次循環;
}
break;
};
r--; r--;
};
}
inline int cmp(const void *a, const void *b) {
return(*(int *)a - *(int *)b);
}
int main()
{
LARGE_INTEGER num;
long long start, end;
double freq;
QueryPerformanceFrequency(&num);
freq = num.QuadPart;
srand((unsigned)time(NULL));
int d1[MAX_SIZE],d2[MAX_SIZE],d3[MAX_SIZE];
int * VK[vksize];
long long sum;
int i;
for (int times = 0; times < TEST_TIMES; ++times)
{
sum = 0;
for (i = 0; i <MAX_SIZE; ++i)
{
d1[i] = rand() % MAX_SIZE;
// d1[i] = i;
d2[i] = d1[i];
d3[i] = d1[i];
sum += d1[i];
}
QueryPerformanceCounter(&num);
start = num.QuadPart;
VK[0] = d1;
VK[1] = &d1[MAX_SIZE - 1];
xsort(VK, &VK[vksize - 1]);
QueryPerformanceCounter(&num);
end = num.QuadPart;
for (i = 0; i < MAX_SIZE - 1; ++i)
{
sum -= d1[i];
if (d1[i] > d1[i + 1])
{
printf("algorithm failed\n");
for (int j = 0; j < MAX_SIZE - 1; ++j)
{
printf("%d,", d1[j]);
}
printf("%d", d1[MAX_SIZE - 1]);
return 1;
}
}
sum -= d1[i];
printf("xsort: %f,%lld ", ((double)(end - start)) / freq,sum);
QueryPerformanceCounter(&num);
start = num.QuadPart;
qsort(d2, MAX_SIZE, sizeof(d2[0]), cmp);
QueryPerformanceCounter(&num);
end = num.QuadPart;
printf("qsort: %f; ", ((double)(end - start)) / freq);
QueryPerformanceCounter(&num);
start = num.QuadPart;
sort(d3, d3 + MAX_SIZE);
QueryPerformanceCounter(&num);
end = num.QuadPart;
printf("sort: %f \n", ((double)(end - start)) / freq);
}
getchar();
return 0;
}
採用隨機數組測試:
採用有序數組測試 :