一、数组实现
①选择排序
从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
。返回所选整数。
解题思路
计数排序使数组升序,若存在一个数不等于最小值且不等于最大值,直接返回它,没找到则返回-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
,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。 请你返回排序后的数组。
解题思路
定义哈希表记录元素的出现次数(频率),然后根据题干条件用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
的任意可能的重新排列。若不可行,返回空字符串 ""
。
解题思路
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° 更新链表的头指针指向已排序区间的头指针
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;
}
②冒泡排序
从链表的头部开始,依次比较相邻的两个节点,如果前一节点大于后一节点,则交换这两个节点的值。每轮遍历,最大的元素会被交换到链表的末尾。然后,再次从头部开始,重复上述操作,直到整个链表排序完成。
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是升序的,直到遍历完原链表。最后将头指针指向已排序链表的头节点。
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; // 将头指针指向已排序链表的头节点
}