查找

顺序查找:

时间复杂度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;
}

分块查找/索引顺序查找

step1 先选取各块中的最大关键字构成一个索引表
step2 查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;然后,在已确定的块中用顺序法进行查找。
 /// <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是随机函数,当关键字的长度不等是,采用这种方法比较合适。

⑵根据选择的冲突处理方法解决地址冲突;

解决冲突的方法有以下两种:
1, 开放地址法
如果两个数据元素的哈希值相同,则在哈希表中为后插入的数据元素另外选择一个表项。
当程序查找哈希表时,如果没有在第一个对应的哈希表项中找到符合查找要求的数据元素,程序就会继续往后查找,直到找到一个符合查找要求的数据元素,或者遇到一个空的表项。
①线性探查法:设发生冲突的地址是d,则用平方探查法所得到的新的地址序列为d+1,d+2......
②平方探查法:设发生冲突的地址是d,则用平方探查法所得到的新的地址序列为d+12,d+22......
2,链地址法
链地址法是把所有的同义词用单链表连接起来的方法。在这种方法中,Hash表每个单元中存放的不再是记录本身,而是相应同义词单链表的表头指针。

⑶在哈希表的基础上执行哈希查找。

        /// <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.

posted @ 2019-03-28 16:51  松花酿酒春水煎茶  阅读(148)  评论(0编辑  收藏  举报