查找
顺序查找
折半查找\二分查找
- 前提条件:有序序列
1-------100 key=88
50---100
75-100
int arr[]={12,23,33,45,66,78,99};
key=79
12,23,33,45,66,78,99
0 6
low mid high
66, 78, 99
mid+1 mid high
low
99
low
high
mid
//查找:low<high
// low==high
while(low<=high)
{
int mid=(low+high)/2
if(key==arr[mid])
找到mid
else if(key>arr[mid])
{
low=mid+1;
}
else
{
high=mid-1;
}
}
哈希表
- 散列表(哈希表),是根据关键码值而直接进行访问的数据结构。也就是说,它通过把关键码映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
词典就是哈希表的一种案例
散列函数的构建
-
直接定址法:取关键字或关键字的某个线性函数值为哈希地址
-
数字分析法:通过分析取关键字的若干数位组成哈希地址
-
平方取中法:取关键字平方后的中间几位数组成哈希地址
-
除留余数法:取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址
H(key)=key MOD 13(p<=m)
哈希表表长m:数组长度除以3/4
p:p一般取不大于表长m的最大质数 -
随机数法:使用随机函数random构建哈希函数
哈希冲突
- 哈希冲突:不同关键字通过哈希函数映射在同一个存储位置
- 开放定址法:线性探测法、二次探测法、伪随机探测法、再哈希法、链地址法、建立公共溢出区
哈希创建
哈希释放空间
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
typedef int datatype;
//哈希表借助以单链表实现
typedef struct Node
{
//数据域
datatype data;
//指针域
struct Node *next;
}*node;
/*
* function: 计算最大质数
* @param [ in]
* @param [out]
* @return 返回质数
*/
int max_prime(int m)
{
//6:1 2 3 6
for(int i=m;i>=2;i--)
{
int count=0;
//i:m--2
//判断i是否是质数,如果是则返回,不是则继续
for(int j=2;j<=sqrt(i);j++)
{
if(i%j==0)
{
count++;//计算2-sqrt(i)之间约数的个数
break;
}
}
if(count==0)
return i;
}
}
node create_node()
{
node s=(node)malloc(sizeof(struct Node));
if(NULL==s)
return NULL;
s->data=0;
s->next=NULL;
return s;
}
/*
* function: 哈希表的插入
* @param [ in]
* @param [out]
* @return
*/
node insert_hash(int key,int p,node hash[])
{
int sub=key%p;
node L=hash[sub];
//创建新节点s
node s=create_node();
s->data=key;
if(L!=NULL)
s->next=L;
L=s;
return L;
}
/*
* function: 哈希表输出
* @param [ in]
* @param [out]
* @return
*/
void output(node hash[],int p)
{
for(int i=0;i<p;i++)
{
printf("%d: ",i);
node L=hash[i];
if(L==NULL)
{
puts("NULL");
continue;
}
while(L!=NULL)
{
printf("%d\t",L->data);
L=L->next;
}
puts("NULL");
}
}
/*
* function: 哈希表查找
* @param [ in]
* @param [out]
* @return 查找成功返回0 失败返回-1
*/
int search_hash(datatype key,int p,node hash[])
{
int sub=key%p;
printf("sub=%d\n",sub);
if(hash[sub]==NULL)
{
return -1;
}
node L=hash[sub];
while(L!=NULL)
{
if(L->data==key)
{
return 0;
}
L=L->next;
}
return -1;
}
node free_space(node hash[],int p)
{
for(int i=0;i<p;i++)
{
while(hash[i]!=NULL)
{
node q=hash[i];
hash[i]=hash[i]->next;
free(q);
q=NULL;
}
}
}
int main(int argc, const char *argv[])
{
//把数组的元素存到哈希表中,在通过哈希表实现查找
int arr[]={67,54,41,67,1093,2345,2345,123,34,123};
int len=sizeof(arr)/sizeof(arr[0]);
int m=len*4/3;//10
int p=max_prime(m);
//构建哈希表
node hash[p]; //指针数组,存储p个指针
for(int i=0;i<p;i++)
{
hash[i]=NULL;
}
//把数组元素存到哈希表
//循环数组元素存到哈希表
for(int i=0;i<len;i++)
{
hash[arr[i]%p]=insert_hash(arr[i],p,hash);
}
//输出哈希表
output(hash,p);
//查找
datatype key;
printf("please enter find data:");
scanf("%d",&key);
int flag=search_hash(key,p,hash);
if(flag==0)
puts("success");
else
puts("error");
//释放
free_space(hash,p);
output(hash,p);
return 0;
}
递归
- 递归算法在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的放发。递归式方法可以被用于解决很多的计算机科学问题,因此它是计算机科学中十分重要的一个概念。绝大多数编程语言支持函数的自调用,在这些语言中函数可以通过自身来进行递归。计算理论可以证明递归的作用可以完全取代循环,因此在很多函数编程语言中习惯用递归来实现循环。
- 直接递归:函数自己调用自己的方式
- 间接递归:多个函数之间相互调用
- 死递归:类似于死循环
- 递归:递归出口/递归边界、递归前进段/递归公示
计算阶乘
递归实现斐波那契
树
树的概念
-
树:是由根节点和若干棵子树构成的树形结构
是n(n>=0)个节点的有限集,n>0时有且只有一个根节点,除根节点外,其余结点构成的互不相交的集合仍是一棵树 -
空树:不含任何节点的树n=0
-
根节点:只有一个节点n=1
-
普通树n>1:多个节点
有序树:有序树【从上到下,从左到右】是树种每棵子树从左到右排列有一定顺序,不能互换的树
无序树:无序树是树中每棵子树从左到右排列无顺序,能互换的树
子树:是树中一个节点以及其下面所有的节点构成的树
树的类型
- 节点:树中的数据元素
- 节点的度:节点含有子树的个数
- 根节点:没有双亲节点【前驱】的节点
- 分支节点(内部节点):是度不为0的节点
- 叶节点(终端节点):是度为0的节点
- 节点的层数:是从根节点到某节点所路径上的层数【根节点表示第一层】
- 树的深度(高度):是树中所有节点层数的最大值
- 树的度:各节点度的最大值
节点之间的关系
- 父节点:是指当前节点的直接上级节点
- 孩子节点:是指当前节点的直接下级节点
- 兄弟节点:是指相同父节点的节点
- 祖先节点:是当前节点的直接及间接上级节点
- 子孙节点:是当前节点直接及间接下级节点
- 堂兄弟节点:是父节点在同一层的节点
森林
- 森林是指0个或多个互不相交树的集合
二叉树
二叉树的概念
-二叉树:树的度小于等于2
-二叉树是每个节点最多有左右两棵子树且严格区分顺序的树形结构【二叉树不可以互换】
- 二叉树左边:左子树是以当前节点的左孩子节点作为根节点的子树
- 二叉树右边:右子树是以当前节点的右孩子节点作为根节点的子树
二叉树的特殊形态
二叉树的类型
-
空二叉树:空二叉树是没有结点的二叉树
-
斜树:斜树是所有的结点都只有左子树或者都只有右子树的二叉树
左斜树:左斜树是所有的结点都只有左子树的斜树
右斜树:右斜树是所有的结点都只有右子树的斜树 -
满二叉树:满二叉树是最后一层是叶子结点,其余结点度是2的二叉树
(1)叶子结点只能出现在最下面一层
(2)非叶子结点的度数一定是2
(3)同样深度的二叉树中,满二叉树的结点的个数最多,叶子结点最多
多 -
完全二叉树:完全二叉树是在一棵满二叉树基础上自左向右连续增加叶子结点得到的二叉树
(1)只有最后两层有叶子结点
(2)除最后一层是一棵满二叉树
(3)最后一层的叶子结点集中在左边连续的位置
二叉树的性质
- 在非空二叉树的第i层上,至多有2^(i-1)个节点(i>=1)
- 在深度为K的二叉树上总结点最多有(2^k)-1个节点
- 在任意一棵二叉树中,叶子节点的数目比度数为2的节点的数目多1
- 完全二叉树结点的编号方法是从上到下,从左到右根节点为1号结点设完全二叉树的结点数为n,某结点编号为I
若 i=1,则该结点是二叉树的根,无双亲,否则,其双亲结点是编号为 i/2的结点
若 2i>n,则该结点无左孩子,否则,其左孩子结点是编号为 2i 的结点
若 2i+1>n,则该结点无右孩子结点,否则,其右孩子结点是编号为2i+1 的结点
二叉树的存储形式
二叉树遍历
先序遍历:按照根左右的顺序
中序遍历:按照左根右的顺序
后序遍历:按照左右根的顺序
二叉树操作
二叉树创建
二叉树先序遍历
二叉树中序遍历
二叉树后序遍历
计算各节点的个数
void Count(Btree tree,int *n0,int *n1,int *n2)
{
if(tree==NULL)
return;
if(tree->lchild==NULL && tree->rchild==NULL)
(*n0)++;//++*n0
else if(tree->lchild!=NULL && tree->rchild!=NULL)
(*n2)++;
else
(*n1)++;
Count(tree->lchild,n0,n1,n2);
Count(tree->rchild,n0,n1,n2);
}
计算树的深度
int High(Btree tree)
{
if(tree==NULL)
return 0;
int left=High(tree->lchild)+1;
int right=High(tree->rchild)+1;
return left>right?left:right;
}
释放二叉树空间
void free_space(Btree tree)
{
if(tree==NULL)
return ;
free_space(tree->lchild);
free_space(tree->rchild);
free(tree);
tree=NULL;
}
快速排序
void free_space(Btree tree)
{
if(tree==NULL)
return ;
free_space(tree->lchild);
free_space(tree->rchild);
free(tree);
tree=NULL;
}