Data structure experiment 9 (sorting algorithm)

Of course, this is also the last experiment of data structure. It has brought us deep thinking through the previous 9 experiments. Now it should be greatly improved for programming algorithms and very good for problem solving. I think now for me, the programming ability has made a qualitative leap. Thank you for this data structure course, which makes me understand a lot! Of course, the later course "algorithm design and analysis" is also very important (although many majors do not open it, it seems that it is only opened in software engineering, so it needs self-study). It is another breakthrough in programming ability. I also hope that students who have completed this course can also learn the course of algorithm. Recommended textbook: Li Chunbao, "algorithm design and analysis (Second Edition)" , Tsinghua University Press.
Finally, summarize the previous data structure experiments!
Experiment 1 Application of linked list (city information)
Application of Experiment 2 stack (purchase information)
Experimental algorithm of triple binary tree (recursive and non recursive)
Experiment 4 Huffman coding
Experiment 6 AOE critical path
Experiment 7 search application (name search)
Experiment 8 search algorithm (balanced binary tree + Hash)

This data structure experiment is about 7 sorting algorithms. I recommend quick sorting and heap sorting. These two sorting methods are also the sorting methods of C++ STL sort function. Here are the performance comparison of various sorting methods. At present, six sorting algorithms have been sorted out, among which the cardinality sorting will be updated later. Let me slow down! I have to do level 6 tomorrow
Baidu network disk extraction algorithm test code Extraction code: ss6l

Sorting methodAverage time complexitySecondary storage space
Simple sort O ( n 2 ) O(n^{2}) O(n2)O(1)
Quick sort O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlog​n)O(log(n)
Heap sort O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlog​n)O(1)
Merge sort O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlog​n)O(n)
Cardinality sortO(d(n+rd))O(rd)

First, the structure code is given

typedef struct{
int key;
}RecordList;

Detailed comments are attached to the following codes, so no explanation is given, only the most refined language is given!
Your third company is the biggest driving force for my creation!

Direct insert sort

Direct insertion, how do you think, is to sort with the previous number from the second number, and then change the position.

void InsertSort(RecordList r[],int n){
for(int i=2;i<=n;i++){
//Why do i start with 2 because i=1 doesn't have to be sorted
count++;//Count each trip
r=r[i];//Set sentinel and put the to be sorted in r
int j=i-1;//j represents the position that has been sorted previously
while(r.key<r[j].key){
//Sort from small to large here
//Move j backward, because it has been sorted in front, so it stops when it is greater than
r[j+1]=r[j];
j--;
}
r[j+1]=r;//Now insert the sentry into the j+1 position
//Now perform the output of each trip
cout<<"The first"<<count<<"Trip sort:";
for(int k=1;k<=n;k++){
cout<<r[k].key<<" ";
}
cout<<endl;
}
}

Bubble sorting

How small the bubble is, it is very simple, that is, select the smallest one in the ith position (i=1,2... n) in each trip

void BubbleSort(RecordList r[],int n){
bool change=true;//Set flag so you don't have to do useless loops
for(int i=1;i<=n-1&&change;i++){
count++;
change=false;//If you have finished sorting, it will become false and jump out in the next cycle
//But I don't think it's enough to let j start from i+1?
for(int j=1;j<=n-1;j++){
if(r[j].key>r[j+1].key){
swap(r[j],r[j+1]);
change=true;
}
}
//Now perform the output of each trip
cout<<"The first"<<count<<"Trip sort:";
for(int k=1;k<=n;k++){
cout<<r[k].key<<" ";
}
cout<<endl;
}
}

Quick sort

How do you think about quick sorting? First select a benchmark, which is generally the first number, and then sort from right and left. Pay attention to starting from right, then encounter something smaller than the benchmark, then start from left, encounter something larger than the benchmark, and then exchange the newly selected ones until the middle. At this time, a benchmark has been completed, and then divide and conquer recursion can be used.

//One quick sort process
int QKPass(RecordList r[],int low,int high){
count++;
//Divide r[low] and r[high] to obtain the reference position
RecordList x=r[low];//Now start with low and save it in X, that is, x will be used as the benchmark to divide the array. The smaller one is on the left and the larger one is on the left
//The key step is to start with pointer j and then i
while(low<high){
while(low<high&&r[high].key>=x.key){
high--;
}
//Why does low < high appear here every time, because each adjustment needs to see whether the benchmark is in place. If not, adjust it
if(low<high){
r[low]=r[high];
low++;
}
while(low<high&&r[low].key<x.key){
low++;
}
if(low<high){
r[high]=r[low];
high--;
}
}
r[low]=x;
//Now perform the output of each trip
cout<<"The first"<<count<<"Trip sort:";
for(int k=1;k<=N;k++){
cout<<r[k].key<<" ";
}
cout<<endl;
return low;
}

void QuickSort(RecordList r[],int low,int high){
if(low<high){
int pos=QKPass(r,low,high);
//Sorting by divide and conquer actually has a taste similar to Hill sorting
QuickSort(r,low,pos-1);
QuickSort(r,pos+1,high);
}
}

Select sort

What do you think of sorting? In short, it is to select the smallest one on the left. The idea is similar to bubble sorting, but bubble is a number to the end, and selection sorting is the exchange of adjacent numbers.

void SelectSort(RecordList r[],int n){
for(int i=1;i<=n-1;i++){
//Why start with 1? Unlike insertion sorting, start with 2, because i=1 also needs to choose the smallest one in the first place
count++;
int k=i;//Save the location at this time i
for(int j=i+1;j<=n;j++){
//Different from bubble sorting, j starts from i+1, because the previous ones have been sorted, so only the latter ones need to be sorted
if(r[j].key<r[k].key){
k=j;//If the following is smaller than the i-th bit at this time, the position of j at this time is saved
}
}
if(k!=i){
swap(r[i],r[k]);
}
//Now perform the output of each trip
cout<<"The first"<<count<<"Trip sort:";
for(int k=1;k<=N;k++){
cout<<r[k].key<<" ";
}
cout<<endl;
}
}

Heap sort

What do you think of heap sorting? First of all, you should have the basic knowledge of binary tree, especially hierarchical traversal. Only when you know the child parent array of binary tree can you understand the creation of algorithm. Note that the essence is to select the parent node from left to right, and then treat the root node as the bottom of the heap.

//Screening method: change one heap
void sift(RecordList r[],int k,int m){
//This is a binary tree array traversed hierarchically. r[k] is the root, r[2k] left and r[2k+1] right. Why? Reference binary tree child parent storage
RecordList t=r[k];//Where to save the root
int x=r[k].key;
int i=k;//Save the position i of pointer k
int j=2*i;//Note that it only refers to the left subtree of i, traversing from left to right first
bool finished=false;//Why set flag, similar to bubbling, to avoid invalid and redundant loops
while(j<m&&!finished){
//What is m? M is the bottom of the pile
//First, compare the left and right subtrees. If the size of the left and right subtrees is different, the pointer changes to a right subtree
if(j<m&&r[j].key<r[j+1].key) j++;
//true if the parent node is larger than the child node, otherwise the location will be exchanged. Note that this is a change
if(x>=r[j].key) finished=true;
else{
r[i]=r[j];
i=j;
j=2*i;
}
}
//Insert t into the i position at this time. If it is not changed, it will still be the original position. If it is changed, it will be converted
r[i]=t;
}

//Build heap algorithm
void crt_heap(RecordList r[],int n){
for(int i=n/2;i>=1;--i){
//Why start with n/2? Binary tree. The textbook takes the middle order traversal as the root. It needs to be changed every time
sift(r,i,n);
}
}

//For heap sorting, the first two steps are preliminary work, which is equivalent to changing all root nodes every time
void HeapSort(RecordList r[],int n){
crt_heap(r,n);
for(int i=n;i>2;i--){
count++;
RecordList b=r;//First position
r=r[i];
r[i]=b;
sift(r,1,i-1);
//Now perform the output of each trip
cout<<"The first"<<count<<"Trip sort:";
for(int k=1;k<=N;k++){
cout<<r[k].key<<" ";
}
cout<<endl;
}
}

Merge sort

What do you think of merging and sorting? Do you feel Hill sort? It feels incremental. This is the essence of splitting from the middle, sorting, and merging one by one.

void Merge(RecordList r1[],int low,int mid,int high,RecordList r[]){
count++;
//Merge from the middle, and save r1[low..mid] and r1[mid+1...high] permutations in r []
int i=low,j=mid+1,k=low;//k as the low bit of r
while((i<=mid)&&(j<=high)){
//Divide into two paths from the middle, and then sort the two paths
if(r1[i].key<=r1[j].key){
//If it is smaller than the middle, it is placed in r[low]
r[k]=r1[i];
i++;
}
else{
r[k]=r1[j];
j++;
}
k++;//r [] traversal creation
}
while(i<=mid){
r[k]=r1[i];
k++;
i++;
}
while(j<=high){
r[k]=r1[j];
k++;
j++;
}
}

void MergeSort(RecordList r1[],int low,int high,RecordList r[]){
//After sorting, it is placed in r [] and r2 is used as the auxiliary space
count++;
RecordList *r2;
r2=new RecordList[high-low+1];
if(low==high) r[low]=r1[low];
else{
int mid=(low+high)/2;
MergeSort(r1,low,mid,r2);
MergeSort(r1,mid+1,high,r2);
Merge(r2,low,mid,high,r);
}
delete r2;
}

summary

The above sorting algorithms are the most basic algorithms. Of course, the sorting algorithm can be further optimized. There is time to come up with an optimization algorithm. These algorithms are the basic skills of data structure. Although it is easy to understand, the code implementation is still difficult, especially in fast, merging and heap sorting. For the understanding of sorting, I feel that we should pay attention to the characteristics of grasping the pointer (position), and when to change the position and when to exchange.

Keywords: Algorithm data structure

Added by Peredy on Fri, 17 Dec 2021 20:41:49 +0200