1.堆排序
1.1 优化堆排序
#define swap(a, b) {a=a^b;b=a^b;a=a^b}
void Adjust2(int arr[], int len, int n_root_id)
{
int MAX;
for(MAX = LEFT; MAX < len; MAX = LEFT)
{
if(RIGHT < len)
{
if(arr[RIGHT] > arr[MAX])
{
MAX = RIGHT;
}
}
if(arr[MAX] > arr[n_root_id])
{
swap(arr[MAX], arr[n_root_id])
n_root_id = MAX;
continue;
}
else
{
break;
}
}
}
2.哈希查找(Hash Search)
2.1 哈希查找的关键是:建立哈希表
2.2 建立哈希表的关键是确定哈西散列函数,一般采用求整取余法
2.3 哈希冲突
- 当我们对101和11分别取余后,两个元素的key值都为1,这是我们就无法判断是将哪一个放入对应的value中去了,这种情况称之为哈希冲突;因此我们需要一定的方法来解决这个问题。
2.4 确定解决哈希冲突问题的方法
- 1.开放地址法
- 1.1 线性探测:向后遍历查找
- 1.2 线性补偿探测:等间隔查找,这种方法可能引起死循环
- 1.2.1 线性探测再散列:意思就是我们对当前key值进行 ±1 、 ±4 、 ±9 等等的搜索之后进行放入;
- 1.3 随机探测:这个方法当我们在查找元素时就不会很方便
- 2.拉链法:以链表的形式将同属于一个key值的元素挂到一个链表中;
2.5 哈西查找速度快,但占用空间较大
2.6 对海量数据查找一般使用 AVL 或者 红黑树
2.7 实现哈希查找
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int n_value;
int n_index;
struct node* p_next;
}Hash;
Hash** CreateHashTable(int arr[], int len)
{
if(arr == NULL || len <= 0)return NULL;
Hash** pp_hash = NULL;
pp_hash = (Hash**)malloc(sizeof(Hash*)*len);
memset(pp_hash, 0, sizeof(Hash*)*len);
int i;
int index;
Hash* p_temp = NULL;
for(i=0; i<len; i++)
{
index = arr[i]%len;
p_temp = (Hash*)malloc(sizeof(Hash));
p_temp->n_value = arr[i];
p_temp->n_index = i;
p_temp->p_next = NULL;
p_temp->p_next = pp_hash[index];
pp_hash[index] = p_temp;
}
return pp_hash;
}
int HashSearch(Hash** pp_hash, int len, int find_num)
{
if(pp_hash == NULL)return -1;
int index = find_num%len;
Hash* p_node = pp_hash[index];
while(p_node)
{
if(p_node->n_value == find_num)
{
return p_node->n_index;
}
p_node = p_node->p_next;
}
return -1;
}
3.桶排序
3.1 什么是桶排序
- 工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。
3.2 适用范围
- 只适用于[0,1)之间的小数。当然我们可以将任意数据向该区间中转换;这个排序算法要求所有的数的位数完全相同。
3.3 实现
typedef struct bucket
{
int n_value;
struct bucket* p_next;
struct bucket* p_pre;
}Bucket;
void Sort(Bucket* p_bucket)
{
if(p_bucket == NULL || p_bucket->p_next == NULL)return;
Bucket* p_unsort = p_bucket->p_next;
Bucket* p_sort = NULL;
int temp;
while(p_unsort != NULL)
{
p_sort = p_unsort->p_pre;
temp = p_unsort->n_value;
while(p_sort != NULL && p_sort->n_value > temp)
{
p_sort->p_next->n_value = p_sort->n_value;
p_sort = p_sort->p_pre;
}
if(p_sort == NULL)p_bucket->n_value = temp;
else
{
p_sort->p_next->n_value = temp;
}
p_unsort = p_unsort->p_next;
}
}
void BucketSort(int arr[], int len)
{
int n_max, n_min;
int i;
int n_max_index, n_min_index;
n_max = arr[0];
n_min = arr[0];
for(i=1; i<len; i++)
{
if(arr[i] > n_max)n_max = arr[i];
if(arr[i] < n_min)n_min = arr[i];
}
int n_count = 1;
int n_num = n_min;
whlie(n_num)
{
n_count *= 10;
n_num /= 10;
}
n_count /= 10;
n_min_index = n_min/n_count;
n_max_index = n_max/n_count;
Bucket** pp_bucket = NULL;
pp_bucket = (Bucket**)malloc(sizeof(Bucket*)*(n_max_index-n_min_index+1));
memset(pp_bucket, 0, sizeof(Bucket*)*(n_max_index-n_min_index+1));
Bucket* p_temp = NULL;
for(i=0; i<len; i++)
{
n_num = arr[i]/n_count - n_min_index;
p_temp = (Bucket*)malloc(sizeof(Bucket));
p_temp->n_value = arr[i];
p_temp->p_next = NULL;
p_temp->p_pre = NULL;
p_temp->p_next = pp_bucket[n_num];
if(pp_bucket[n_num] != NULL)
pp_bucket[n_num]->p_pre = p_temp;
pp_bucket[n_num] = p_temp;
}
for(i=0; i<n_max_index-n_min_index+1; i++)
{
Sort(pp_bucket[i]);
}
n_num = 0;
for(i=0; i<n_max_index-n_min_index+1; i++)
{
p_temp = pp_bucket[i];
while(p_temp)
{
arr[n_num] = p_temp->n_value;
n_num++;
p_temp = p_temp->p_next;
}
}
Bucket* p_del = NULL;
for(i=0; i<n_max_index-n_min_index+1; i++)
{
p_temp = pp_bucket[i];
while(p_temp)
{
p_del = p_temp;
p_temp = p_temp->p_next;
free(p_del);
p_del = NULL;
}
}
free(pp_bucket);
pp_bucket = NULL;
}
4.基数排序
4.1 什么是基数排序
4.2 按照低位优先还是高位优先可以分为两种方法:LSD和MSD
4.3 步骤
- 1.先找出最大的元素的位数;
- 2.将所有元素按照个位进行入桶,不变顺序,再放入原数组中;
- 3.将所有元素按照十位进行入桶,不变顺序,再放入原数组中;
- 4.依次进行,知道到达最高位;
- 5.将桶中的元素出桶,就可完成排序;
4.4 实现
typedef struct node
{
int n_value;
struct node* p_next;
}Radix;
void RadixSort(int arr[], int len)
{
if(arr == NULL || len <= 0)return;
int n_max = arr[0];
int i;
for(i=0; i<len; i++)
{
if(arr[i] > n_max)n_max = arr[i];
}
int n_loop_times = 0;
int n_num = n_max;
while(n_num)
{
n_loop_times++;
n_num /= 10;
}
Radix** pp_radix = NULL;
pp_radix = (Radix**)malloc(sizeof(Radix*)*10);
memset(pp_radix, 0, sizeof(Radix*)*10);
int n_base = 1;
n_max = 0;
Radix* p_temp = NULL;
Radix* p_temp = NULL;
for(i=1; i<n_loop_times; i++)
{
n_num = i;
while(n_num > 1)
{
n_base *= 10;
n_num--;
}
for(n_max=0; n_max<len; n_max++)
{
n_num = arr[i]/n_base%10;
p_temp = (Radix*)malloc(sizeof(Radix));
p_temp->n_value = arr[n_max];
p_temp->p_next = NULL;
if(pp_radix[n_num] == NULL)
{
pp_radix[n_num] = p_temp;
}
else
{
p_node = pp_radix[n_num];
while(p_node->p_next != NULL)
{
p_node = p_node->p_next;
}
p_node->p_next = p_temp;
}
}
n_num = 0;
for(n_max=0; n_max<10; n_max++)
{
p_node = pp_radix[n_max];
while(p_node)
{
arr[n_num] = p_node->n_value;
n_num++;
p_node = p_node->p_next;
}
}
for(n_max=0; n_max<10; n_max++)
{
p_node = pp_radix[n_max];
while(p_node)
{
p_temp = p_node;
p_node = p_node->p_next;
free(p_temp);
p_temp = NULL;
}
}
memset(pp_radix, 0, sizeof(Radix*)*10);
}
}
5.时间复杂度
5.1 时间复杂度是同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率;算法分析的目的在于选择合适算法和改进算法
5.2 一般采用“大O标记法”来表示算法的时间复杂度
5.3 常数事件的时间复杂度是O(1)
5.4 判断下述算法的时间复杂度
for(i=0; i<n; i++)
- 这个for循环一共执行了n次,再加上for循环中的常数语句,因此总得执行次数为 n+c ,则时间复杂度为:O(n)
5.5 判断下述算法的时间复杂度(总的执行次数为k)
for(i=1; i<=n; i*=2)
{
}
5.6 两层for循环的时间复杂度
for(i=0; i<n; i++)
{
for(j=0; j<i; j++)
{
}
}
5.7 三层for循环
for(i=0; i<n; i++)
{
for(j=0; j<i; j++)
{
for(k=0; k<j; k++)
{
}
}
}
5.8 二叉树前序遍历的时间复杂度
5.5 各个排序算法的时间复杂度、空间复杂度、稳定性等总结
排序方式 | 最好时间复杂度 | 最坏时间复杂度 | 平均时间复杂度 | 空间复杂度 | 稳定性 | 复杂性 |
---|
插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 | 简单 |
希尔排序 | O(n^1.2~1.5) | | | O(1) | 不稳定 | 较复杂 |
冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 | 简单 |
快速排序 | O(nlog(2)n) | O(n^2) | O(nlog(2)n) | O(log(2)n) | 不稳定 | 较复杂 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 | 简单 |
堆排序 | O(nlog(2)n) | O(n^2) | O(nlog(2)n) | O(1) | 不稳定 | 较复杂 |
归并排序 | O(nlog(2)n) | O(n^2) | O(nlog(2)n) | O(n) | 稳定 | 较复杂 |
基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O® | 稳定 | 较复杂 |