一、数组实现

①选择排序

从index=0开始,每一轮找到一个最小的元素,然后交换num[index]和num[min]的位置,直至数组遍历完。得到一个升序数组。

void selectSort(int* num, int n){
    for(int i = 0; i < n; i++){
        int min = i;
        for(int j = i + 1; j < n; j++){
            if(num[j] < num[min]){
                min = j;
            }
        }
        int tmp = num[i];
        num[i] = num[min];
        num[min] = tmp;
    }
}

②冒泡排序

交换n-1轮,每一轮都从前往后开始对数组进行两两交换,那么每轮都把当轮最大的数放置在最后边,保证升序。

void bubbleSort(int* num, int n){
	for(int i = n-1; i>=0; i--){
        for(int j = 0; j < i ; j++){
            if(nums[j] > nums[j+1]){
                int tmp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = tmp;
            }
        }
    }
}

③插入排序

1、将第一个数作为已排序列(已排序列里都是升序的),第二个数以后的作为待排序列。

2、从第二个数开始,和已排序列进行比较,如果当前数大于它的前一个数,直接把当前数归为已排序列。如果当前数小于它的前一个数,那么将当前数的前一个数的值赋值为当前数(相当于往后移动一位),重复此操作直到保证当前数比前一个数要大,然后当前数归为已排序列。

3、重复步骤2直到所有数归为已排序列,得到升序数组。

void insertSort(int* nums, int n){
    for(int i = 1; i < n; i++){
        int tmp = nums[i];
        int j = i-1;
        while(j>=0){
            if(tmp>nums[j]){
                nums[j+1] = nums[j];
                j--;
            }else{
                break;
            }
        }
        nums[j+1] = tmp;
    }
}

④计数排序

先遍历一遍数组统计每个元素的出现次数,并记录最小值作为偏移量。即hash[当前数-最小值]++,统计完后再遍历哈希表重新赋值到数组中,如:若hash[0]>0,那么num[0] = 0+偏移量(最小值),hash[0]--

以下结合例题加深理解:

2733. 既不是最小值也不是最大值 - 力扣(LeetCode)

给你一个整数数组 nums ,数组由 不同正整数 组成,请你找出并返回数组中 任一 既不是 最小值 也不是 最大值 的数字,如果不存在这样的数字,返回 -1 。返回所选整数。

基础排序——数组和链表实现(C)_C语言

解题思路

计数排序使数组升序,若存在一个数不等于最小值且不等于最大值,直接返回它,没找到则返回-1

代码实现

void countSort(int *nums, int n){
    int cnt[101];
    memset(cnt,0,sizeof(cnt));
    for(int i = 0; i < n; i++){
        cnt[nums[i]]++;
    }
    // 再输出到原数组中
    int size = 0;
    for(int i = 0; i < 101; i++){
        while(cnt[i]){
            nums[size++] = i;
            cnt[i]--;
        }
    }
}

int findNonMinOrMax(int* nums, int numsSize){
    countSort(nums, numsSize);
    for(int i = 1; i < numsSize-1; i++){
        if(nums[i]!=nums[0] && nums[i]!=nums[numsSize-1]){
            return nums[i];
        }
    }
    return -1;
}
1636. 按照频率将数组升序排序 - 力扣(LeetCode)

给你一个整数数组 nums ,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。 请你返回排序后的数组。

基础排序——数组和链表实现(C)_学习笔记_02

基础排序——数组和链表实现(C)_学习笔记_03

解题思路

定义哈希表记录元素的出现次数(频率),然后根据题干条件用qsort函数设置排序条件,注意hash[201]以及 *(int*)p1 + 100这些防溢出等数据偏移的操作

代码实现

int hash[201];
int cmp(const void *p1, const void *p2){
	// 将指针转换为整数并加上100,防止溢出
    int tmpa = *(int*)p1 + 100;
    int tmpb = *(int*)p2 + 100;
     // 比较两个数在哈希表中的计数
     // 如果两个数的计数相等(即频率相等),则降序排列
    if(hash[tmpa]==hash[tmpb]){
        return tmpb - tmpa;
    }
     // 否则根据计数大小排序
    return hash[tmpa]-hash[tmpb];
}

int* frequencySort(int* nums, int numsSize, int* returnSize){
    memset(hash, 0, sizeof(hash));
    int i = 0;
    for(i = 0; i < numsSize; i++){
        hash[nums[i]+100]++;
    }
    qsort(nums, numsSize, sizeof(int), cmp);
    *returnSize = numsSize;
    return nums;
}
767. 重构字符串 - 力扣(LeetCode)

给定一个字符串 s ,检查是否能重新排布其中的字母,使得两相邻的字符不同。

返回 s 的任意可能的重新排列。若不可行,返回空字符串 "" 。基础排序——数组和链表实现(C)_数据结构与算法_04     基础排序——数组和链表实现(C)_C语言_05

解题思路

1° 特殊情况处理,当字符串长度小于2时,直接返回它

2° 统计每个字符的出现次数,若出现次数最多的字符超过 int(整体长度+1) 的一半,直接返回空

3° 设置奇偶指针,指向新字符串的奇数位置和偶数位置,然后依次遍历哈希表,对于每个字母:①如果出现次数大于0,hash[i] > 0;②其出现次数不小于字符串长度的一半;③下标odd不越界;将其按照奇数位置填充到新字符串中。

①③好理解,主要是②,为什么?简单举例即可理解:给定一个字符串 "aaabb",此时a的出现次数显然超过了字符串长度的一半,那么只能是 "ababa"的排法,首尾必须是a,这种特殊情况,出现次数最多的字符必须放在首尾的位置,就不能是放在奇数位置。

4° 然后再将其按照偶数位置填充到新字符串中,直到其出现次数为零。

5° 最后记得添加字符串结束符"\0",并返回。

代码实现

char* reorganizeString(char* s) {
    int len = strlen(s);

    if(len < 2){
        return s;
    }

    int hash[26];
    memset(hash,0,sizeof(hash));
    int max = 0; 

    for(int i = 0; i < strlen(s); i++){
        hash[s[i]-'a']++;
    }

    for(int i = 0; i < 26; i++){
        max = hash[max] > hash[i] ?  max : i;
    }

    if(hash[max] > (len+1)/2){
        return "";
    }

    char *ans= (char*)malloc(sizeof(char)*(len+1));

    int odd = 1, even = 0;

    for(int i = 0; i < 26; i++){
        char c = 'a' + i;
        while(hash[i] && hash[i] <= len/2 && odd < len){
            ans[odd] = c;
            hash[i]--;
            odd+=2;
        }
        while(hash[i]){
            ans[even] = c;
            hash[i]--;
            even+=2;
        }
    }
    ans[len] = '\0';
    return ans;
}

二、链表实现

方式一:修改节点的指针          方式二:交换节点的值

以下示例都只选用了一种方式实现。

①选择排序

1° 需要一个指针head记录未排序区间,和一个指针sortedHead记录已排序区间。还需要多个指针进行辅助修改,最小值指针min,最小值指针的前一个指针preMin,当前指针cur,当前指针的前一个指针pre,并进行初始化。循环直到链表为空,即所有节点都已排序。

2° 每轮遍历,找到未排序区间的最小值节点及其前驱节点。

3° 将最小值从未排序区间中删除

4° 将最小值节点添加到已排序区间的末尾

5° 更新链表的头指针指向已排序区间的头指针

基础排序——数组和链表实现(C)_C语言_06

基础排序——数组和链表实现(C)_学习笔记_07

void selectionSort(Node** head) {
    Node *sortedHead = NULL;
    while(*head){
        Node* preMin = NULL;
        Node* min = *head;
        Node* pre = NULL;
        Node* cur = *head;

        while(cur && cur->next){
            if(cur->next->data < min->data){
                preMin = cur;
                min = cur->next;
            }
            pre = cur;
            cur = cur->next;
        }

        if(preMin){
            preMin->next = min->next;
        }else{
            *head = min->next;
        }

        if(!sortedHead){
            sortedHead = min;
        }else{
            Node *tmp = sortedHead;
            while(tmp->next){
                tmp = tmp->next;
            }
            tmp->next = min;
        }
        min->next = NULL;
    }
    *head = sortedHead;
}

②冒泡排序

从链表的头部开始,依次比较相邻的两个节点,如果前一节点大于后一节点,则交换这两个节点的值。每轮遍历,最大的元素会被交换到链表的末尾。然后,再次从头部开始,重复上述操作,直到整个链表排序完成。

基础排序——数组和链表实现(C)_数据结构与算法_08

void bubbleSort(Node **head) {
    int swapped;  // 标记是否有未排序元素被交换
    Node *p;
    Node *lastP = NULL; // 标记每轮的最大元素存放位置,作为下一轮的终止条件
    if (!*head)
        return;
    swapped = 1;  // 设为1,表示有未排序元素

    while (swapped) {
        swapped = 0;  // 表示未排序元素被交换过了,如果内循环后仍未变化,则退出排序
        p = *head;  // 每一轮从头节点开始交换
        while (p->next != lastP) {  // 一次排序过程,内循环一直到尾节点
            if (p->data > p->next->data) {
                int temp = p->data;
                p->data = p->next->data;
                p->next->data = temp;
                swapped = 1;  // 有未排序元素未被交换,设为1,表示还需要循环排序
            }
            p = p->next;
        }
        lastP = p;  // 重置尾节点为p
    }
}

③插入排序

将链表分为已排序(part 1)和未排序(part 2)两部分,每轮往已排序区间里丢入未排序的头节点,这个过程始终保持part 1是升序的,直到遍历完原链表。最后将头指针指向已排序链表的头节点。

基础排序——数组和链表实现(C)_数据结构与算法_09

基础排序——数组和链表实现(C)_学习笔记_10

void insertSort(Node **head) {
    Node *sorted = NULL;  // 已排序链表,起始指向空
    Node *cur = *head;  // 当前未排序节点的指针,起始指向头节点

    while (cur) {  // 当链表未排序节点不为空时,开始进行排序
        Node *tmp = cur->next; // 存储当前节点的下一个节点,作为下一轮未排序的头节点
        if (!sorted || cur->data < sorted->data) {  // 当排序链表为空或者当前节点的值小于排序链表的头节点,则把当前节点插入到表头位置
            cur->next = sorted;
            sorted = cur;
        } else {  // 否则在已排序链表中找到第一个值大于当前节点的节点,并插入到它之前
            Node *temp = sorted;
            while (temp->next  && cur->data > temp->next->data) {  // 遍历已排序链表,找到要插入的位置
                temp = temp->next;
            }
            cur->next = temp->next;
            temp->next = cur;
        }
        cur = tmp;  // 指针指向下一个未排序节点
    }
    *head = sorted;  // 将头指针指向已排序链表的头节点
}