【Data Structure Learning Record 27】——Selection Sorting and Heap Sorting

1. Selection sort

1. Principle

The selection and sorting is relatively simple, that is, after each traversal, a maximum (minimum) value of the disordered area is exchanged with the first or last value of the disordered area. In this way, the disordered area gradually becomes ordered, and finally Complete the sorting. Its time complexity is also O (n 2) O(n^2)O ( n2)

2. Process

Insert picture description here

3. Code

int selectsort(int arry[], int len)
{
    
    
    int min, i, j, temp;

    for(i = 0; i < len - 1; ++i)
    {
    
    
        min = i;
        for (j = i; j < len; ++j)
        {
    
    
            if (arry[j] < arry[min])
            {
    
    
                min = j;
            }
        }
        temp = arry[i];
        arry[i] = arry[min];
        arry[min] = temp;
    }
}

2. Heap sort

1. The concept of heap

Suppose the sequence k 1, k 2, ⋅ ⋅ ⋅, kn (k_1,k_2,\cdot\cdot\cdot,k_n)k1,k2,,knThere are and only when the following relations are met, we call it a heap:
{ki ≤ k 2 i, ki ≤ k 2 i + 1 or {ki ≥ k 2 i, ki ≥ k 2 i + 1 where (i = 1, 2, ⋅ ⋅ ⋅, n 2) \begin{cases} k_i ≤ k_{2i}, \\ k_i ≤ k_{2i+1} \end{cases} or \begin{cases} k_i ≥ k_{2i}, \ \ k_i ≥ k_{2i+1} \end{cases} where (i = 1,2,\cdot\cdot\cdot,\frac{n}{2}){ kik2 I,kik2 I + 1or{ kik2 I,kik2 I + 1Which the ( I=1,2,,2n)
Note that the sequence number starts from 1.
The former is less than or equal to the relationship, so we also call it: the 小根堆
latter is greater than or equal to the relationship, so we also call it:大根堆

2. The relationship between heap and complete binary tree

Then if we simulate a complete binary tree through this sequence (array), under the tree structure, the heap has the following properties:
each parent node is larger (less than) its son node, see the picture:
suppose there is this small root heap :
Insert picture description here

To "convert" it into a complete binary tree:
Insert picture description here
It is not difficult to see that the child nodes of node n are 2n and 2n+1

3. The process of heap sorting

We now know that the root node of this heap binary tree (simulated by sequential sequence) must be 最大(小), then the element must be in an ordered state. Then our sorting idea should be:

  1. Construct a heap
  2. Exclude the root node element (exclude the top element or exchange with the last element and exclude)
  3. Re-adjust the structure so that the remaining elements form a pile
  4. Repeat 2 and 3 until all elements are in order

Of course, in the second point, in order to prevent the basic tree structure from being destroyed, we choose to place the top element and the end element 交换after the pile top element , and classify the end element as 有序区, that is, exclude, and do not participate in the re-adjustment of point 3. structure.
Therefore, on this basis, we generally use ascending order 大根堆and descending order 小根堆.
After complicated derivation, its time complexity is O (nlog 2 n) O(nlog_2n)O ( n l o g2n)

4. Construct a heap

To initialize the heap, we need the following steps:
Perform the following 调整堆operations on all parent nodes :

  1. If the child nodes are all smaller than the parent node, it ends.
  2. In the case of non-1: the maximum value of the parent node and the child node is exchanged, and operations of 1 and 2 are performed on the child node after the exchange.

We know that assuming a complete binary tree full of leaves, suppose it has n nodes, then the number of leaf nodes is (n + 1) 2 \frac{(n+1)}{2}2(n+1), Then the parent node has (n − 1) 2 \frac((n-1))(2)2(n1)One
Then, suppose we initialize the heap, we start from the last parent node and move forward (from right to left, from bottom to top) to construct the heap in a loop.
From the code point of view:

    // 从最后一个父结点开始,将所有结点给调整一次。
    for (i = len/2; i > 0; --i)
    {
    
    
        HeapAdjust(heapArry, i, len);
    }

5. Adjust the heap

We adjusted the heap, thoughts and said:

  1. If the child nodes are all smaller than the parent node, it ends.
  2. In the case of non-1: the maximum value of the parent node and the child node is exchanged, and operations of 1 and 2 are performed on the child node after the exchange.

code show as below:

int HeapAdjust(int arry[], int index, int len)
{
    
    
    int i;
    arry[0] = arry[index]; // arry[0] 没被使用,刚好可以拿来当交换时的临时变量用
    for (i = 2*index; i <= len; i*=2)   // 从该结点的左孩子开始,且每次循环都直接到孩子的孩子,且i肯定不能超过树的大小
    {
    
    
        if (i < len && arry[i] < arry[i+1]) // 这里是判断i当前孩子是左孩子大还是右孩子大,将i指向最大的孩子
        {
    
    
            ++i;
        }
        // 把上者<改成>,下者>=改为<=,则该堆排序变成降序
        if (arry[0] >= arry[i]) // 如果我们的处理的结点大于了孩子结点,那么就必须交换。
        {
    
    
            break;
        }
        else
        {
    
    
            arry[index] = arry[i];  // 我们处理的结点被孩子顶替
            index = i;              // 我们新处理的结点变成了被顶替的孩子位置
        }
    }
    arry[index] = arry[0];  // 一直到了最后,孩子一直顶替,直到没法顶替了,就是我们最开始待处理结点的位置
}

Legend:
Suppose we only have the root node not adjusted, now we need to adjust the node with index=1:
Insert picture description here
first come in and determine our element status: 5 is to be adjusted, 7 is the current point to the son:
Insert picture description here
at this time, [0 ]<[i], that is, 5<7, because there should be an adjustment, so [1] should be equal to [2], and then the value to be adjusted has changed from [1] to [2], and the pointed son has changed to [4] ]:
Insert picture description here
At this time, [0]<[i], that is, 5<6, because adjustment should occur, so [2] should be equal to [4], and then the adjustment to be adjusted has changed from [2] to [4], pointing to The son of has changed to [8] (does not exist):
Insert picture description here

Then after exiting the loop, we have determined the position to be adjusted index=4, so temp is the value of arry[0] to arry[4]:
Insert picture description here

This completes a heap adjustment.

6. Code

For the specific process, you can output HeapSort halfway to see the process of heap sorting and construction.

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

int HeapAdjust(int arry[], int index, int len)
{
    
    
    int i;
    arry[0] = arry[index]; // arry[0] 没被使用,刚好可以拿来当交换时的临时变量用
    for (i = 2*index; i <= len; i*=2)   // 从该结点的左孩子开始,且每次循环都直接到孩子的孩子,且i肯定不能超过树的大小
    {
    
    
        if (i < len && arry[i] < arry[i+1]) // 这里是判断i当前孩子是左孩子大还是右孩子大,将i指向最大的孩子
        {
    
    
            ++i;
        }
        // 把上者<改成>,下者>=改为<=,则该堆排序变成降序
        if (arry[0] >= arry[i]) // 如果我们的处理的结点大于了孩子结点,那么就必须交换。
        {
    
    
            break;
        }
        else
        {
    
    
            arry[index] = arry[i];  // 我们处理的结点被孩子顶替
            index = i;              // 我们新处理的结点变成了被顶替的孩子位置
        }
    }
    arry[index] = arry[0];  // 一直到了最后,孩子一直顶替,直到没法顶替了,就是我们最开始待处理结点的位置
}

int HeapSort(int arry[], int len)
{
    
    
    int i = 1;

    int *heapArry = (int*)malloc(sizeof(int) * (len+1));    // 构造一个从下标1开始的序列。
    for (i = 1; i <= len; ++i)
    {
    
       
        heapArry[i] = arry[i - 1];
    }

    // 从最后一个父结点开始,将所有结点给调整一次。
    for (i = len/2; i > 0; --i)
    {
    
    
        HeapAdjust(heapArry, i, len);
    }

    // 堆排序。
    for (i = len; i > 0; --i)
    {
    
    
        arry[i-1] = heapArry[1];    // 堆顶是我们的最大元素,赋值给原数组
        heapArry[1] = heapArry[i];  // 因为是交换,所以要把最后一个元素给堆顶,堆顶给最后一个元素(有序),但我们可以舍弃这个保存,因为存到了老数组里
        HeapAdjust(heapArry, 1, i - 1); // i之后的结点是有序的(尽管没有赋值),所以不参与堆的构造
    }
}

int main()
{
    
    
    int a[7] = {
    
    5,6,3,7,2,1,4};
    int i;
    HeapSort(a, 7);
    for (i = 0; i < 7; ++i)
    {
    
    
        printf("%d ", a[i]);
    }
    return 0;
}

Guess you like

Origin blog.csdn.net/u011017694/article/details/111504356