8.3 内部排序法---交换类排序(冒泡、快排)

来源:互联网 发布:mac迅雷下载提示版权 编辑:程序博客网 时间:2024/04/30 04:48

交换类排序:主要是不断的比较两个元素的大小并交换其原始的位置,完成一趟排序之后,最大的元素(或最小的元素)被排在最后(或者最前)。重复以上操作若干趟后,元素序列成为一个有序序列。主要有冒泡排序快速排序。时间复杂度分别为:O(n^2)O(nlogn)

冒泡排序思想依次比较两个相邻的数,把较小的数放在前面,把较大的数放在后面。

快速排序的思想:快排不是比较两个相邻的元素,而是将指定的元素与序列中的元素依次比较,将逆序的两个元素交换

下面是简单的冒泡排序和快排的简单操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/***********************************************************/
// 程序名称:SwapSort.cpp
// 程序目的:内部排序法---交换式排序
// 程序来源:数据结构与算法分析(C语言版) P-237
// 日期:2013-9-4 15:20:37 JohnnyHu修改
/***********************************************************/

#include <stdio.h>
#include <stdlib.h>

#define Error( str )        FatalError( str )
#define FatalError( str )   fprintf( stderr, "%s\n", str ), exit( 1 )

#define  MAX    5   
typedef int ElementType;

void Swap(ElementType& lValue, ElementType& rValue)
{
    ElementType tmp = lValue;
    lValue = tmp;
    lValue = rValue;
    rValue = tmp;
}

void BubbleSort(ElementType a[], int n);
void InsertionSort(ElementType a[], int n);
void QuickSort1(ElementType a[], int n);
void PrintSort(ElementType a[], int n);

int main(void)
{
    int data[MAX] = {891245833};

    printf("排序前: ");
    PrintSort(data, MAX);

    //BubbleSort(data, MAX);
    QuickSort1(data, MAX);
    //QuickSort2(data, MAX);

    printf("排序后: ");
    PrintSort(data, MAX);

    return 0;
}

/************************************************************************/
// 冒泡排序
/************************************************************************/

void BubbleSort(ElementType a[], int n)
{
    bool bChanged;      // 记录数值位置是否交换
    bChanged = true;
    for (int i = n-1; i > 1 && bChanged; i--)
    {
        bChanged = false;
        for (int j=0; j < i; j++)
        {
            if (a[j] > a[j+1])
            {
                Swap(a[j], a[j+1]); // 较大的数据始终在后面
                bChanged = true;
            }
        }
    }

    return;
}

/************************************************************************/
// 直接插入插入排序
/************************************************************************/

void InsertionSort(ElementType a[], int n)
{
    ElementType tmp;
    for (int i = 1; i < n; i++)
    {
        tmp = a[i];     // 取出第i个元素

        int j = i;
        for ( ; j > 0 && a[j-1] > tmp; j--) 
        { // 从后往前寻找插入位置
            a[j] = a[j - 1];
        }
        a[j] = tmp;     // a[i]插入正确位置
    }
}

// 快排的核心部分
void Qsort1(ElementType a[], int low, int high)
{
    int left = low; 
    int right = high;
    ElementType pivot = a[low]; // 轴枢元素
    while (left < right)
    {
        while (left < right && pivot <= a[right])
            right--;        // 从后往前找小于pivot的元素
        if (left < right)
        {   // 将小于pivot的元素a[right]存入a[left]
            a[left] = a[right];
            left++;
        }

        while (left < right && pivot > a[left])
            left++;     // 从前往后找大于pivot的元素
        if (left < right)
        {
            a[right] = a[left];
            right--;
        }
    }
    a[left] = pivot;    // 轴枢元素存入a[left]

    if (low < left)
        Qsort1(a, low, left - 1);
    if (left > high)
        Qsort1(a, right+1, high);

    return;
}
/************************************************************************/
// 快速排序1
/************************************************************************/

void QuickSort1(ElementType a[], int n)
{
    int low, high;

    low = 0;
    high = n - 1;
    Qsort1(a, low, high);

    return;
}

/************************************************************************/
// 打印排序表
/************************************************************************/

void PrintSort(ElementType a[], int n)
{
    for (int i = 0; i < n; i++)
        printf("[%d]\t", a[i]);
    printf("\n");

    return;
}
输出结果:



当然冒泡排序也可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/************************************************************************/
// 冒泡排序
/************************************************************************/

void BubbleSort(ElementType a[], int n)
{
    //bool bChanged;        // 记录数值位置是否交换
    //bChanged = true;
    //for (int i = n-1; i > 1 && bChanged; i--)
    //{
    //  bChanged = false;
    //  for (int j=0; j < i; j++)
    //  {
    //      if (a[j] > a[j+1])
    //      {
    //          Swap(a[j], a[j+1]); // 较大的数据始终在后面
    //          bChanged = true;
    //      }
    //  }
    //}

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n - i - 1; i++)
        {
            if (a[j] > a[j=1])
                Swap(a[j], a[j+1]);
        }
    }

    return;
}
很简单的一个二重循环,但就效率来见,肯定不如上面的经典下面,但二重循环更易理解。


上面的快排1是每次排序时均将第一个元素作为轴枢元素,是最为简洁的一种,其实,轴枢元素的选择有多种方法,轴枢元素的选择对快排算法的效率有着重大的影响,所以上面的快排1有一定的缺陷,下面介绍快排2,改变主意针对的是轴枢元素的选择采用三数值分割策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/************************************************************************/
// 直接插入插入排序
/************************************************************************/

void InsertionSort(ElementType a[], int n)
{
    ElementType tmp;
    for (int i = 1; i < n; i++)
    {
        tmp = a[i];     // 取出第i个元素

        int j = i;
        for ( ; j > 0 && a[j-1] > tmp; j--) 
        { // 从后往前寻找插入位置
            a[j] = a[j - 1];
        }
        a[j] = tmp;     // a[i]插入正确位置
    }
}

// 3数中值分割法
ElementType Median3(ElementType a[], int letf, int right)
{
    int center = (letf + right) / 2;

    if (a[letf] > a[center])
        Swap(a[letf], a[center]);
    if (a[letf] > a[right])
        Swap(a[letf], a[right]);
    if (a[center] > a[right])
        Swap(a[center], a[right]);
    // 此时a[left] <= a[center] <= a[right]

    Swap(a[center], a[right - 1]);  // 将枢纽元放到数组right-1的位置

    return a[right - 1];        // 放回枢纽元
        
}
 
// 快排核心部分
#define  CUTOFF     3
void Qsort2(ElementType a[], int left, int right)
{
    ElementType pivot;      // 枢纽元

    if (left + CUTOFF <= right) 
    { // 保证数组中有3个以上的元素
        pivot = Median3(a, left, right);
        int i = left;
        int j = right - 1;

        for ( ; ; )
        {
            while (a[++i] < pivot) { }
            while (a[--j] > pivot) { }
            if (i < j)
                Swap(a[i], a[j]);
            else 
                break;
        }
        Swap(a[i], a[right - 1]);   // 将pivot在存储在right-1中

        Qsort2(a, left, i - 1);
        Qsort2(a, i + 1, right);
    }
    else    // 在子数组上j进行插入排序
        InsertionSort(a + left, right - left + 1);

    return;
}

/************************************************************************/
// 快速排序2
/************************************************************************/

void QuickSort2(ElementType a[], int n)
{
    Qsort2(a, 0, n - 1);
}
相对于快排1,快排2在平均处理数据的速度上,占有一定优势。


原创粉丝点击