Linux与数据结构 2019-4-13

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;

	// 找待查找值所对应的key值
	int index = find_num%len;

	// 通过key值遍历对应的key值上的线性表
	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;
	}

	// 对应key值上的线性表没有查找到待查找值,返回-1
	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)
{
	// code
}
  • 时间复杂度为:O(log(2)n)

5.6 两层for循环的时间复杂度

for(i=0; i<n; i++)
{
	for(j=0; j<i; j++)
	{
		// code
	}
}
  • 两层for循环的时间复杂度为:O(n^2)

5.7 三层for循环

for(i=0; i<n; i++)
{
	for(j=0; j<i; j++)
	{
		for(k=0; k<j; k++)
		{
			// code
		}		
	}
}
  • 3层for循环的时间复杂度为:O(n^3)

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))稳定较复杂
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值