顺序查找:
时间复杂度O(n)
遍历整个列表,逐个进行比较:
int SequenceSearch(int[] arr, int key) { for (int i = 0; i < arr.Length; i++) { if (arr[i] == key) { return i; } } return -1; }
二分查找/折半查找:
二分查找要求原有数组有序 时间复杂度为O(logn)
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
int Binary_Search(int a*,int n,int key) { int low,high,mid; low=1; /*定义最底下标为记录首位*/ high=n; /*定义最高下标为记录末位*/ while(low<=high) { mid=(low+high)/2; /*折半*/ if(key<a[mid]) high=mid-1; if(key>a[mid]) low=mid+1; else return mid; } return 0; }
分块查找/索引顺序查找
/// <summary> /// 分块 /// </summary> /// <param name="arr"></param> ///<param name="blockSize">每块大小</param> /// <returns></returns> public static IndexItem[] DividBlock(int[] arr, int blockSize) { int totalBlock = (int)Math.Ceiling(arr.Length * 1.0 / blockSize);//块的总数 IndexItem[] blocks = new IndexItem[totalBlock]; //确定每一块的信息 int j = 0;//数组的索引 int k = 0; int s = 0; for (int i = 0; i < totalBlock; i++) { s = j * blockSize; k = s + blockSize - 1; blocks[i].start = s; if (k>arr.Length - 1) { k = arr.Length - 1; } blocks[i].end = k; blocks[i].key = arr[k]; j++; } return blocks; } public static int IndexSearch(int[] arr,int key,int blockSize) { IndexItem[] indexItem = DividBlock(arr, blockSize);//对数组进行分块,得到索引列表 int i=0; while (i < indexItem.Length && key > indexItem[i].key)//从索引列表中查找key所在的索引列表 { i++; } if (i >= indexItem.Length) return -1; int j = indexItem[i].start; int k = indexItem[i].end; for (int l = j ; l <=k; l++)//根据key所在的索引列表的索引进行顺序查找key在数组中的位置 { if (key == arr[l]) { return l; } } return -1; } } /// <summary> /// 索引表:存储主表分块后的每一块信息 /// </summary> public struct IndexItem { public int key;//存放对应块中的最大关键字 public int start;//存放对应块的第一个元素位置 public int end;//存放对应块的最后一个元素位置 }
哈希查找:
⑴用给定的哈希函数构造哈希表;
六种哈希函数的构造方法:
1,直接定址法:
取关键字或关键字的某个线性函数为Hash地址,即H(key)=key或者H(key)=a*key+b,其中a,b为常数
这种方法的优点是:简单,均匀,不会产生冲突。但是需要事先知道关键字的分布情况,适合查找表较小并且连续的情况。
2,数字分析法:
假设关键字是r进制(如十进制数),并且Hash表中可能出现的关键字都是事先知道的,则可选取关键字的若干数位组成Hash地址。选取的原则是使得到的Hash地址尽量避免冲突,即所选数位上的数字尽可能是随机的。
3,平方取中法:
取关键字平方后的中间几位作为Hash地址。通常在选定Hash函数的时候不一定能知道关键字的全部情况,仅取其中的几位为地址不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此,得到的Hash地址随机性更大,取的位数由表长决定。
故名思义,比如关键字是1234,那么它的平方就是1522756,再抽取中间的3位就是227作为哈希地址。
4,折叠法:
折叠法是将关键字从左到右分割成位数相等的几个部分(最后一部分位数不够可以短些),然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。
比如我们的关键字是9876543210,哈希表表长三位,我们将它分为四组,987|654|321|0 ,然后将它们叠加求和987+654+321+0=1962,再求后3位即得到哈希地址为962,哈哈,是不是很有意思。
5,除留余数法:
函数公式:f(key)=key mod p (p<=m)m为哈希表表长。
这种方法是最常用的哈希函数构造方法。
6,随机数法:
函数公式:f(key)= random(key)。
这里random是随机函数,当关键字的长度不等是,采用这种方法比较合适。
⑵根据选择的冲突处理方法解决地址冲突;
⑶在哈希表的基础上执行哈希查找。
/// <summary> /// hash表插入 /// </summary> /// <param name="hastTable"></param> /// <param name="key"></param> public static void InsertHashTable(int[] hashTable, int key) { int hashAddress = Hash(hashTable, key); while (hashTable[hashAddress] != 0) { hashAddress = (++hashAddress + 1) % hashTable.Length; } hashTable[hashAddress] = key; } /// <summary> /// hash查找 /// </summary> /// <param name="hashTable"></param> /// <param name="key"></param> public static int HashSearch(int[] hashTable, int key) { int hashAddress = Hash(hashTable, key); while (hashTable[hashAddress]!=key) { hashAddress = (hashAddress + 1) % hashTable.Length; if (hashTable[hashAddress] == 0 || hashAddress == Hash(hashTable, key)) return -1; } return hashAddress; } /// <summary> /// hash函数(除留余数法) /// </summary> /// <param name="hashTalbe"></param> /// <param name="data"></param> /// <returns></returns> private static int Hash(int[] hashTalbe, int data) { return data % hashTalbe.Length; }
二叉排序查找:
二叉排序树的定义:
(1)若它的左子树不空,则左子树上所有关键字的值均小于根关键字的值。
(2)若它的右子树不空,则右子树上所有关键字的值均大于根关键字的值。
(3)左右子树又各是一棵二叉排序树。
二叉排序查找:
将待查关键字先和根结点中的关键字比较,如果相等则查找成功;如果小于则到左子树中去查找,无需考虑右子树中的关键字;如果大于则到右子树中去查找,无需考虑左子树中的关键字。如果来到当前树的子树根的时候,重复上述过程;如果来到了结点的空指针域,则说明查找失败。
//定义存储结构 typedef struct BTNode { int key; //表示关键字 struct BTNode *lchild; struct BTNode *rchild; }BTNode; //二叉排序查找 BTNode* BSTSearch(BTNode* bt, int key) { if(bt == NULL) return NULL; else { if(bt->key == key) return bt; else if(key<bt->key) return BSTSearch(bt->lchild,key); else return BSTSearch(bt->rchild,key); } }
给定值的比较次数等于给定值节点在二叉排序树中的层数。如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2n+1,其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。
平衡二叉树:
平衡二叉树又称为AVL树,是一种特殊的二叉排序树。其左右子树都是平衡二叉树,且左右子树高度之差的绝对值不超过1.即以树中所有结点为根的左右子树高度之差的绝对值不超过1
为了判断一棵二叉排序树是否是平衡二叉树,引进了平衡因子的概念。平衡因子是针对树中的结点来说的,一个结点的平衡因子为其左子树的高度减去右子树高度的差。对于平衡二叉树,树中的所有的结点平衡因子的取值只能是-1、0、1.