[数据结构][C++] 查找和排序(哈希表存储基本思想)

哈希表类概念摘要

哈希表类SqHash的建立、查找。设有若干个学生的考试成绩,采用除留余数求哈希地址,将学生的信息存储到该地址空间,并且采用线性探测法解决冲突问题。

哈希表又称散列表。

哈希表存储的基本思想是:以数据表中的每个记录的关键字 k为自变量,通过一种函数H(k)计算出函数值。把这个值解释为一块连续存储空间(即数组空间)的单元地址(即下标),将该记录存储到这个单元中。在此称该函数H为哈希函数或散列函数。按这种方法建立的表称为哈希表或散列表。

除留余数法:

取关键字k被某个不大于表长m的数p除后所得余数作为哈希函数地址的方法。即:

          H(k)=k  mod p      

这种方法的关键是选择好p。使得数据集合中的每一个关键字通过该函数转化后映射到哈希表的任意地址上的概率相等。理论研究表明,一般取p为小于m的最大质数或不包含小于20的质因素的合数。

线性探测再散列:

当发生冲突时,将依次探测“下一个位置”,直到找到其关键字相匹配的元素或找到一个空位插入。设哈希空间长度为m,“下一个位置”由下式确定:

   Hi=(H(key)+di) mod m

   H(key):哈希函数

   m:哈希表长度

   di:求“下一个位置”的增量

di=1,2,…,m-1。

这种di的取法称为线性探测再散列。即“下一个位置”为哈希表的直接后继。若当di=m-1时仍未查到,则说明表满,还要查找另外的溢出表。缺点:容易产生“二次聚集” 

我来简单总结一下这个究竟讲了什么
这次的内容是查找和排序
那么跟查找有关的有哈希,跟排序有关的有冒泡排序,快速排序
关于哈希的工作原理,大家可以仔细阅读上面那段话
那么下面先贴出的是

哈希查找的演示代码

#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <iomanip.h>
//---------------------------------------------------------------------------
typedef int KeyType;       // 关键字的类型
const int MAXSIZE=100;     // 数组的容量
struct ElemType            //学生的记录信息
{ KeyType  key ;        //学号
char name[8];         //姓名
int english;          //成绩
int math;             //成绩
} ;
class SqHash
{ private:
ElemType *ht;     // 哈希表数组
int length;       // 哈希表容量
KeyType   p;      // 除留余数法的大质数
public:
        SqHash( int n1,int p1);
        ~SqHash(){delete []ht;  length=0;};
        int  find( KeyType K );
        void  creat_hash();
        void PrintOut();
};
//-------------------------------------------------------------
SqHash::SqHash(int n1,int p1)
{ length=n1;  p=p1;
ht=new ElemType[length];
for(int i=0;i<length;i++)ht[i].key=0;
}
void  SqHash::creat_hash()
{ int i,j,K,en,ma;char na[8];
cout<<"\n  请逐一输入各个学号(关键字值)(-1结束):"; cin>>K;
while(K!=-1)
{  i=find(K);
if(i==-32767)  cout<<"\n   成功查找到,不再插入!";
else  if(i!=32767){
        cout<<"\n  请输入学生的姓名,英语成绩和高数成绩:";    cin>>na>>en>>ma;
        ht[i].key=K;
        strcpy(ht[i].name,na);     //用串拷贝赋值
        ht[i].english=en;
        ht[i].math=ma;              // 插入学生记录K
        cout<<"\n   插入成功!" ;
        cout<<"\n  请逐一输入各个学号(关键字值)(-1结束):"; cin>>K;
}
}
}
int  SqHash::find( KeyType K )
{ 
        int i,j;
        j=K%p;
        i=j-1;
        while((ht[j].key!=0)&&(ht[j].key!=K)&&(j!=i)) 
                j=(j+1)%MAXSIZE;//解决冲突
        if(ht[j].key==K)
        {
                cout<<"\n  成功查找到学号为"<<K<<"的学生,位置在:"<<j<<"上";
                cout<<"\n\n 该学生的基本信息为:\n\n";
                cout<<"姓名:"<<ht[j].name<<"英语成绩:"<<ht[j].english<<"高数成绩:"<<ht[j].math;
                j=-32767;
        }
        else if(j==i){
                cout<<"\n 表满,出现异常! OverFlow!";//溢出
                j=32767;
        }
        return j;
        
        
        
}
void  SqHash::PrintOut()
{ int i,j;
for (i=0;i<length; i++)
cout<<"\n  i="<<i<<"   学号:"<<setw(6)<<ht[i].key<<"   姓"<<setw(6)<<ht[i].name
<<"   英语成绩:"<<setw(6)<<ht[i].english<<"   高数成绩:"<<setw(6)<<ht[i].math;
}
int main()
{  int p0,n0;
cout<<"\n  请输入n值(n值应是记录总数的1.3-1.5倍)"; cin>>n0;
cout<<"\n  请输入P值(应是不大于n 的大质数):"; cin>>p0;
SqHash  ha(n0,p0);  ElemType a;
int k; char ch;
do { cout<<"\n\n\n";
cout<<"\n       1. 建立哈希表(开放地址法)  ";
cout<<"\n       2. 在哈希表中查找某位学生 ";
cout<<"\n       3. 输出哈希表";
cout<<"\n       4. 结束";
cout<<"\n=======================================";
cout<<"\n       输入您的选择(1,2,3,4):"; cin>>k;
switch(k)
{ case 1:{  ha.creat_hash();
}  break;
case 2:{  cout<<"\n  请输入待查找的学生学号:";  cin>>a.key;
        int i=ha.find(a.key);
        if(i!=-32767) cout<<"\n  此学生"<<a.key<<"  不存在!" ;
           }   break;
case 3:{ ha.PrintOut(); }  break;
}
}while(k>=1&&k<=3);
_getch();        return 0;
}

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法

它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。

这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

冒泡法排序演示例子

#include<iostream.h>
class intsz
{
public:
        int i;
};

int main()
{
        int j,t;
        intsz i[100];
        cout<<"请输入你需要输入的数据个数总数:";
        cin>>j;
        for(int a=0;a<j;a++)
        {
                cout<<"请输入第"<<a+1<<"个数值:";
                cin>>i[a];
        }
        for(int b=0;b<j;b++)
                for(int c=0;c<j;c++)
                {
                        if(i[b]<i[c])
                        {
                                t=i[b];
                                i[b]=i[c];
                                i[c]=t;
                        }
                }
        
        cout<<"从小到大排序:"<<endl;
        for(a=0;a<j;a++)
        {
                cout<<i[a]<<"  ";
        }

        cout<<endl<<"从大到小排序:"<<endl;
        j--;
        for(a=0;a<j+1;j--)
        {
                cout<<i[j]<<"  ";
        }
        cout<<endl;

        return 0;
                                
}

排序除了冒泡排序外还有快速排序、希尔排序


快速排序概念

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快速排序的算法是:

1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;

2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];

3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;

4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;

5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

快速排序又分为两种,一种是递归,一种是非递归
1.递归快速排序算法:略,如果需要的朋友可以留言(或者自己去百度)
2.非递归快速排序算法:

#include <iostream.h>
#include  <conio.h>
struct node  //记录结构。为简化问题,设定记录中只含关键字
   {int key;
    }a[20];       //a数组是全局变量
int n;            //n也是全局变量
void print( )
{int i;
for (i=0;i<n;i++) cout<<a[i].key<<" ";
            cout<<"\n";
}
void creat()
{int i;   n=0;
cout<<"input keys"; cin>>i;
while (i!=-9999)  {a[n].key=i; n++;
                    cin>>i;
                   }
}
template <class T>
int hoare(T a[20],int l,int h) /*分区处理函数*/
{int i,j;    node x;
i=l;j=h;x.key=a[i].key;
do{   while((i<j) && (a[j].key>=x.key)) j--;
        if(i<j) {a[i].key=a[j].key;i++;}
      while((i<j) && (a[i].key<=x.key)) i++;
        if (i<j) {a[j].key=a[i].key;j--;}
    }while (i<j);
a[i].key=x.key;  return i;
}//hoare end
template <class T>
void quick1(T a[20],int n)  //非递归的快速排序主体函数
{int i,l,h,tag,top;
int s[20][2];
l=0;h=n-1;tag=1;top=0;
do {while (l<h)  {i=hoare(a,l,h);
                   top++;  s[top][0]=i+1;  s[top][1]=h;h=h-1;
                  }
      if(top==0) tag=0;
            else { l=s[top][0];
                       h=s[top][1];  top--;
                      }
}while (tag==1);
cout<<"\n  输出非递归快速排序的结果:"<<endl;
}//quick1 end
int  main()
{  creat();  print();
    quick1(a,n); print();     // a数组和n都是全局变量
_getch();   return 0;
}

希尔排序

希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

该方法实质上是一种分组插入方法

比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。

一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

给定实例的shell排序的排序过程

假设待排序文件有10个记录,其关键字分别是:

49,38,65,97,76,13,27,49,55,04。

增量序列的取值依次为:5,2,1

希尔排序演示代码:

#include <iostream.h>
#include  <conio.h>
struct node  //记录结构。为简化问题,设定记录中只含关键字
{int key;
}a[20];
int n;
void print( )
{ int i;
    for (i=0;i<n;i++)  cout<<a[i].key<<endl
}
void creat()
{int i; n=0;
cout<<"input keys:"; cin>>i;
while (i!=-9999)  {a[n].key=i;n++;
                    cin>>i;
                   }
}
template <class T>
void shell(T a[n],int n)  //希尔排序
{ int i,j,k;
  for(i=n;i>=1;i--) a[i].key=a[i-1].key;
  k=n/2;
  while (k>=1)
  { for (i=k+1;i<=n;i++)
{ a[0].key=a[i].key; j=i-k;
       while((a[j].key>a[0].key)&&(j>=0))
            { a[j+k].key=a[j].key; j=j-k; }
       a[j+k]=a[0];
     }
    k=k/2;
   }
  for (i=0;i<n;i++) a[i].key=a[i+1].key;
  cout<<"\n  输出希尔排序的结果:"<<endl;
}//shell end
int  main()
  {  creat();  print();
     shell(a,n); print();
_getch();  return 0;
}

到这里我们已经基本上已经将常见的和常用的查找算法以及排序算法讲解完了,对于一般人来说,基本掌握这几种算法就可以了。

关于如何去理解哈希算法的思想,这个需要慢慢的理解,初次接触这类算法或多或少都会有很多问题,或者看不懂,不理解的地方。

展开阅读全文

没有更多推荐了,返回首页