线性表的查找
适用于静态查找表
数据类型
1 2 3 4 5 6 7 8 9 10 typedef struct { KeyType key; InfoType otherinfo; }ElemType; typedef struct { ElemType *R; int length; }SSTable;
顺序查找
从表的一端开始,依次将记录的关键字和给定值进行比价,若某个记录的关键字和给定值相等,则查找成功;反之,若扫描整个表后,仍为找到关键字和给定值相等的记录,则查找失败。时间复杂度O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int Search_Seq (SSTable st, KeyType key) { for (int i = st.length; i >= 1 ; i--) { if (st.R[i].key == key) { return i; } } return 0 ; } int Search_Seq2 (SSTable st, KeyType key) { st.R[0 ].key = key; int i = st.length; for (; st.R[i].key != key; i--); return i; }
折半查找
折半查找也称为二分查找,拆半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。时间复杂度\(O(log_2n)\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int Search_Bin (SSTable st, KeyType key) { int low = 1 ; int higt = st.length; while (low < higt) { int mid = (low + higt) / 2 ; if (key == st.R[mid].key) { return mid; } else if (key < st.R[mid].key) { higt = mid - 1 ; } else { low = mid + 1 ; } } return 0 ; }
分块查找
分块查找又称索引顺序查找。在该查找方法中,除表本身以外,尚需建立一个“索引表”。时间复杂度\(O(\sqrt n)\)
步骤
分块 :将数组分成若干个大小相同的块(最后一个块可能较小)
查找块 :通过比较目标值与每个块的起始元素,确定目标值可能所在的块
顺序查找 :在确定的块内进行顺序查找,找到目标值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <math.h> #define BLOCK_SIZE 3 int blockSearch (int arr[], int size, int target) { int blockCount = (int )ceil ((double )size / BLOCK_SIZE); for (int i = 0 ; i < blockCount; i++) { int start = i * BLOCK_SIZE; int end = (i + 1 ) * BLOCK_SIZE < size ? (i + 1 ) * BLOCK_SIZE : size; if (target >= arr[start] && target < arr[end - 1 ]) { for (int j = start; j < end; j++) { if (arr[j] == target) { return j; } } break ; } } return -1 ; }
顺序查找、折半查找和分块查找的比较
比较项目 查找方法
顺序查找
折半查找
分块查找
查找时间复杂度
\(O(n)\)
\(O(log_2n)\)
与确定所在块的查找方法有关
特点
算法简单,对表结构物任何要求,但查找效率低
对表结构要求高,查找效率高
对表结构有一定要求,查找效率介于折半查找和顺序查找之间
使用情况
任何结构的线性表,不经常做插入和删除
有序的顺序表,不经常做插入和删除
块间有序、块内无序的顺序表,经常做插入和删除
树表的查找
适用于动态查找表
二叉排序树(二叉查找树)
时间复杂度
创建树: \(O(nlog_2n)\)
插入结点: \(O(log_2n)\)
查找结点: \(O(log_2n)\)
删除结点: \(O(log_2n)\)
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: -
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 -
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值 -
它的左右子树也分别为二叉排序树
故:
中序遍历一颗二叉排序树可以得到一个结点值递增的有序序列
结构定义
1 2 3 4 5 6 7 8 9 10 typedef int KeyType;typedef struct { KeyType key; }ElemType; typedef struct BSTNode { ElemType data; struct BSTNode *lchild , *rchild ; }BSTNode, *BSTree;
二叉排序树的递归查找
算法步骤
若二叉排序树为空,则查找失败,返回空指针
若二叉排序树非空,将给定值与根结点的关键字t->data.key进行比较:
若key等于t->data.key,则查找成功,返回该结点地址
若key小于t->data.key,则递归查找左子树
若key大于t->data.key,则递归查找右子树
1 2 3 4 5 6 7 8 9 10 11 12 BSTree SearchBST (BSTree t, KeyType key) { if (!t || key == t->data.key) { return t; } if (key < t->data.key) { return SearchBST(t->lchild, key); } return SearchBST(t->rchild, key); }
二叉排序树的插入
算法步骤
若二叉排序树为空,则将插入结点*s作为根结点插入到空树中
若二叉排序树非空,则将key与根结点的关键字t->data.key比较
若key小于t->data.key,则将*s插入左子树
若key大于t->data.key,则将*s插入右子树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdlib.h> BSTree InsertBST (BSTree t, KeyType key) { if (!t) { BSTree s= (BSTree)malloc (sizeof (BSTNode)); ElemType data = {key: key}; s->data = data; s->lchild = s->rchild = NULL ; return s; } if (key < t->data.key) { t->lchild = InsertBST(t->lchild, key); } if (key > t->data.key) { t->rchild = InsertBST(t->rchild, key); } return t; }
二叉排序树的创建
算法步骤
将二叉排序树t初始化为空树
读取一个关键字key的结点
如果读取的关键字不是输入结束标志,则循环执行一下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> #include <stdlib.h> BSTree CreateBST () { BSTree t = NULL ; KeyType e; scanf ("%d" , e); while (e != 65535 ) { t = InsertBST(t, e); scanf ("%d" , e); } return t; }
二叉排序树的删除
首先从二叉排序树的根结点开始查找关键字为key的待删除结点,如果树中不存在此结点,则不做任何操作;否则,假设被删结点为*p
,其双亲结点为*f
,\(p_l\) 和\(p_r\) 分别表示其左子树和右子树。假设*p
是*f
的左孩子,可分下列情况讨论:
若*p
为叶子结点,则\(p_l\) 和\(p_r\) 均为空树。由于删除叶子结点不破坏整棵树的结构,则只需修改其双亲结点的指针即可。
f->lchild=NULL;
若*p
结点只有左子树\(p_l\) 或只有右子树\(p_r\) ,此时只要令\(p_l\) 或\(p_r\) 成为其双亲结点*f
的左子树即可。
f->lchild = p->lchild; 或 f->lchild = p->rchild;
若*p
的左右子树均不为空。在删除*p
之后,为保持其他元素之间的相对位置不变,有以下两种处理方法:
令*p
的左子树为*f
的左子树,而*p
的右子树为*s
的右子树
[可能增加树的深度 ]
f->lchild = p->lchild; s->rchild = p->rchild
令*p
的直接前驱(或直接后继)替代*p
,然后再从二叉排序树中删除它的直接前驱(或直接后继)。当以直接前驱*s
替代*p
时,由于*s
只有左子树\(s_l\) ,则在删去*s
之后,只要令\(s_l\) 为*s
的双亲*q
的右子树即可
[以被删结点左子树中关键字最大的结点替代被删结点,然后从左子树中删除这个结点。此结点一定没有右子树(否则它就不是左子树中关键字最大的结点),这样不会增加树的高度 ]
p->data=s->data; q->rchild=s->lchild;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <stdlib.h> BSTree DeleteBST (BSTree t, KeyType key) { BSTree p = t; BSTree q = NULL ; BSTree f = NULL ; while (p) { if (p->data.key == key) { break ; } f = p; if (p->data.key > key) { p = p->lchild; } else { p = p->rchild; } }; if (!p) { return t; } if (p->lchild && p->rchild) { q = p; BSTree s = p->lchild; while (s->rchild) { q = s; s = s->rchild; } p->data = s->data; if (q != p) { q->rchild = s->lchild; }else { q->lchild = s->lchild; } free (s); } else if (!p->rchild) { q = p; p = p->lchild; } else { q = p; p = p->rchild; } if (!f) { t = p; } else if (q == f->lchild) { f->lchild = p; } else { f->rchild = p; } free (p); return t; }
平衡二叉树
查找时间复杂度\(O(log_2n)\)
平衡二叉树或者是空树,或者是具有如下性质的二叉排序树
左子树和右子树的深度之差的绝对值小于1
左子树和右子树也是平衡二叉树
平衡树
折半查找和二叉排序树查找的比较
比较项目 查找方法
折半查找
二叉排序树的查找
查找时间复杂度
\(O(log_2n)\)
\(O(log_2n)\)
特点
数据结构采用有序的顺序表,插入和删除操作需移动大量元素
数据结构采用树的二叉链表表示,插入和删除操作无需移动元素,只需修改指针
使用情况
不经常做插入和删除的静态查找表
经常做插入和删除的动态查找表
B树
B树
散列表
散列表