2 数据结构.docx
- 文档编号:30129411
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:27
- 大小:22.80KB
2 数据结构.docx
《2 数据结构.docx》由会员分享,可在线阅读,更多相关《2 数据结构.docx(27页珍藏版)》请在冰豆网上搜索。
2数据结构
2数据结构
根据逻辑关系,数据结构可分为4类:
集合、线性结构、数结构、图结构。
两种存储方式:
顺序存储结构和链接存储结构。
2.1算法概述
算法是对特定问题求解步骤的一种描述,算法的5个重要特性:
输入、输出、
有穷性、确定性和可行性。
一个“好”算法还需要具有下列特性:
正确性、鲁棒
性、简单性、抽象分级和高效性。
伪代码是“算法语言”。
1.算法设计的一般原则:
理解问题。
准确的理解算法的输入是什么?
要求算法完成什么?
即明确
算法的入口和出口。
预测所有可能的输入。
包括合法的输入和非法的输入。
列举出算法必须
处理的所有情况。
抽象分级,使解决方案模块化。
可以有效的帮助我们解决复杂问题。
跟踪代码。
发现算法中的逻辑错误的唯一重要的方法就是系统的跟踪算
法。
2.算法时间复杂度的分析方法
找出算法中的基本语句,执行次数最多的语句就是基本语句,通常是最
内层循环的循环体。
计算基本语句执行次数的数量级。
用大O极好表示算法的时间性能。
如果算法中包含嵌套的循环,则基本
语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间
复杂度相加。
2.2线性表(重要)
线性表可以顺序表、单链表、循环表、双链表和静态链表等。
2.2.1单链表
编写用来删除单链表的头元素的函数。
voidRemoveHead(node**head)
{
node*tmp;
//iftheheadisaNULL,donothing.
if(!
(*head))
return;
tmp=(*head)->next;
free(*head);
*head=tmp;
return;
}
函数检查步骤
..查看输入的参数是否合法;
..考虑通常情况下的操作是否达到要求;
..考虑返回值是否达到要求;
..检查异常情况下函数的功能;
..最后检查函数的正确性需要满足如下三个条件:
1.能够完成要求的工作;
2.能够被正确调用,能够产生正确的返回值;
3.能够对异常情况作出适当处理。
1.删除单链表的倒数第m个元素,算法需要即节省时间又节省空间,并对异
常情况做出适当的处理。
解决思路:
单链表,显然我们无法知道其长度n,因此在确定倒数第m个元
素之前,我们首先要找到该链表的尾指针。
那么首先,可以想到的一个算法就是
先遍历该单链表,计算该链表的长度n,然后,我们可以确定倒数第m个元素的
位置为(n-m),然后,再重新从链表的头开始搜索,找到(n-m)个元素后,就
是需要找的。
这种方法的时间复杂度:
O(n)。
空间复杂度:
就是一个辅助指针。
那么这是不是最节省时间和空间的呢?
不见得,我们还可以增加一个辅助指
针变量,使其与遍历指针保持m个距离,即当“前导指针”遍历到第m个元素
时,启动“拖后指针”,使“拖后指针”指向本链表的头指针,然后与“前导指
针”同步移动,那么,当“前导指针”指向链表末时,此时“拖后指针”当前指
向的元素即为“倒数第m个”元素。
找到需要的元素。
这种算法的时间复杂度:
O(n),但是要比前一种方法节省了越一般的时间,因为,前一种方法需要对链表
进行两次遍历,而后这只需要遍历一遍,付出的代价仅仅是增加了一个指针变量。
所以是值得的。
因此,我们可以选择第二种方法。
下面,我们来讨论一下本算法的函数原型:
一般来说,我们总是希望函数的
返回值能够是要找的“倒数第m个”元素,但是,考虑到可能出现的异常情况:
寻找失败,所以,我们可以,令函数返回值为一个错误码,即寻找成功返回1,
失败返回0,然后通过一个指向指针的指针参数来获得所要找的元素。
代码如下,
第一段代码的语义不够简洁,因此我又对其改进,如第二段代码。
//第一种实现代码。
intFindRevM(nodeconst*head,void**getm,intm)
{
node*pre=head;
node*behind=NULL;
inti=0;
if(head==NULL)
{return0;}
while(pre)
{
if(i { i++; pre=pre->next; } if(i==m) { behind=head; } pre=pre->next; behind=behind->next; } if(i {return0;} *getm=behind; return1; } //第二种实现代码。 intFindRevM(nodeconst*head,void**getm,intm) { node*pre=head; node*behind=NULL; inti=0; if(head==NULL) {return0;} for(i=0;i { if(pre->next) { pre=pre->next; } //iftheprepointerreachtheendofchain //beforeireachsm,returnfalse.becausethe //numberofnodesonthechainislessthanm. else {return0;} } //startthebehindpointer. behind=head; while(pre->next) { pre=pre->next; behind=behind->next; } //getm. *getm=behind; return1; } 2.3特殊线性表–堆栈、队列和串(重要) 2.3.1堆栈 栈是限定在表尾进行插入和删除操作的线性表,栈中元素除了具有线性关系外,还具有 先进后出(LIFO,LastinFirstout)的特性。 用C++代码实现堆栈类: Classstack { public: stack(); ~stack(); Voidpush(void*data) Voidpop(); Protected: //Elementstructneededonlyinternally. TypedefstructelementT{ StructelementT*next; Void*data; }elementT; elementT*header; }; //constructureofstackclass. stack: : stack() { Header=NULL; Return; } Stack: : ~stack() { elementT*tmp; While(header) { tmp=header->next; free(header); header=tmp; } Return; } Stack: : push(void*data) { elementT*tmp=newelementT; tmp->data=data; tmp->next=header; header=tmp; return; } Void*stack: : pop() { elementT*tmp; void*data; assert(header! =NULL); data=header->data; tmp=header->next; free(header); header=tmp; returndata; } 2.3.2堆(heap)和堆栈(stack) heap: 是由malloc之类函数分配的空间所在地。 地址是由低向高增长的。 stack: 是自动分配变量,以及函数调用的时候所使用的一些空间。 地址是由 高向低减少的。 一个由c/C++编译的程序占用的内存分为以下几个部分: ①栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量的值等。 其操作方 式类似于数据结构中的栈。 ②堆区(heap) 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。 注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 ③全局区(静态区)(static) 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在 一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 ④文字常量区 常量字符串就是放在这里的。 程序结束后由系统释放 ⑤程序代码区 存放函数体的二进制代码。 2.3.3队列 队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表,队 列中的元素除了具有线性关系外,还具有先进先出(FIFO,FirstinFirstout)的 特性。 2.3.4字符串 串拷贝(strcpy)和内存拷贝(memcpy)有什么不同? 它们适合于在哪种情况下使用? ①strcpy()函数只能拷贝字符串。 strcpy()函数将源字符串的每个字节拷贝到 目录字符串中,当遇到字符串末尾的null字符(\0)时,它会删去该字符,并结束 拷贝。 ②memcpy()函数可以拷贝任意类型的数据。 因为并不是所有的数据都以null 字符结束,所以你要为memcpy()函数指定要拷贝的字节数。 ③在拷贝字符串时,通常都使用strcpy()函数;在拷贝其它数据(例如结构) 时,通常都使用memcpy()函数。 ④在实现的时候需要考虑内存重叠。 数组编程试题: 删除特定字符 用C语言编写一个高效率的函数来删除字符串中特定的字符,函数调用模型 如下: voidRemoveChars(char*str,char*remove) { intsrc,dst,removeArray[256]; //256ASSICcharacters, //Zeroallelementsinarray. for(src=0;src<256;src++) { removeArray[src]=0; } //markthecharstoberemoved. src=0; while(remove[src]) { removeArray[remove[src]]=1; src++; } //Copycharunlessitmustberemoved. src=dst=0; do{//do...whileterminatesafterNUL. if(! removeArray[str[src]] { str[dst++]=str[src]; } }while(str[src++]);} 2.4树和二叉树 注意二叉树节点的五种形态 空、没有子树、只有左子树、只有右子树、左右子树 存储结构 ..顺序存储结构 ..链式存储结构 二叉链表、三叉链表 structnode { intvalue; node*left; node*right; }; 二叉树的创建 node*creat(inta[],intlength) { inti; node*root=NULL; for(i=0;i { root=Insert(root,a[i]); } returnroot; } 插入 node*Insert(node*root,intvalue1) { node*np; if(root==NULL) { np=newnode; np->value=value1; np->right=NULL; np->left=NULL; root=np; returnroot; } if(value1<=root->value) { if(root->left==NULL) { np=newnode; np->value=value1; np->right=NULL; np->left=NULL; root->left=np; returnroot; } else { root->left=Insert(root->left,value1); } } else { if(root->right==NULL) { np=newnode; np->value=value1; np->right=NULL; np->left=NULL; root->right=np; returnroot; } else { root->right=Insert(root->right,value1); } } returnroot; } 删除 需要考虑集中情况,若删除的节点为: ..叶子: 直接删除,修改父节点指针; ..父节点: ..若只有一个子树: 子节点代替其位置 ..若有两个子树―― ..从左子树,向右找到一个右子树为空的节点,将该节点移到 删除节点位置,用其左子树代替其原来位置。 ..又分为以下两种情况: 105->44231513 和 voiddelTree(node*root,intvalue1) { //first,findit node*p,*q; q=NULL;//qistheparentnodeofp p=root; while(p&&(p->value! =value1)) { q=p; if(value1>p->value) p=p->right; else p=p->left; } if(p==NULL) { cout<<"cannotfindthisval"< return; } //checkthetypeofthenode if((p->left==NULL)&&(p->right==NULL)) { if(q==NULL) root=NULL; elseif(value1>q->value) q->right=NULL; else q->left=NULL; deletep; return; } elseif((p->left! =NULL)&&(p->right==NULL)) { if(q==NULL)//therootnode root=root->left; elseif(value1 q->left=p->left; else q->right=p->left; deletep; return; } elseif((p->right)&&(p->left==NULL)) { if(q==NULL) root=p->right; elseif(value1>q->value) q->left=p->right; else q->right=p->right; deletep; return; } else { node*s; q=NULL; s=p->left; //findthebiggestnodelowerthanit while(s->right) { q=s; s=s->right; } if(q==NULL) p->left=s->left; else q->right=s->left; p->value=s->value; p=s; deletep; return; } } 遍历 voidPreOderTraverse(node*root) { if(root==NULL) return; cout< if(root->left! =NULL) PreOderTraverse(root->left); if(root->right! =NULL) PreOderTraverse(root->right); } 测试深度 intDeepOfTree(node*root) { intdeep1,deepL,deepR; if(root==NULL) return0; deepL=DeepOfTree(root->left); deepR=DeepOfTree(root->right); deep1=deepL>deepR? deepL: deepR; returndeep1+1; } 2.5图 (该部分不是特别重要) 图的存储结构 ..数组表示 ..邻接表 ..十字链表 图的遍历 ..深度优先DFS ..广度优先BFS 最小生成树 ..Prim算法 ..Kruskal算法 最短路径问题 2.6查找技术 静态查找: 不涉及插入和删除操作的查找。 动态查找: 涉及插入和删除操作的查找。 用平均查找长度衡量查找算法的时间性能。 折半查找判定树、时空权衡、平衡二叉树。 2.7排序技术(重要) 2.7.1排序算法比较 排序算法的稳定性: 具有相同键值的记录经过排序后其相对次序保持不变, 则稳定。 否则,不稳定。 1.插入排序 将记录插入到已排序序列的正确位置上。 分类: ..直接插入排序: ..依次将待排序序列中的每一个记录插入到一个已排好序的序列中。 ..时间复杂度: 最好情况,最坏情况,平均情况。 ()On2()On2()On ..希尔排序: ..直接插入排序的改进。 先将待排序记录序列分割成若干子序列,在子 序列内分别进行直接插入排序,最后对全体记录进行一次直接插入排 序。 ..时间复杂度: ~。 (log)Onn2()On 2.交换排序 将两个记录进行比较,当反序时进行交换。 ..冒泡排序: ..两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录 为止。 ..时间复杂度: 最好,最坏,平均。 ()On2()On2()On ..快速排序: ..冒泡排序的改进,选定轴值,将待排序记录分割成独立的两部分,左 侧记录的关键码都小于等于轴值,右侧记录都大于等于轴值,然后分 别对左右两部分再分别重复上述过程,直到整个序列有序。 ..时间复杂度: 平均。 (log)Onn 3.选择排序 选择排序: 从未排序序列中选出最小记录放入已排序队列的一端。 ..简单选择排序: ..第i趟通过次关键码的比较,在ni.1ni.+(11)in≤≤.个记录中选取关 键码最小的,并通过与第i个记录交换最为有序序列的第个记录。 i ..时间复杂度: 最好、最坏、平均都是。 2()On ..堆排序: ..简单选择排序的改进。 首先将待排序序列构造成一个堆,然后选择堆 中最大的记录即堆顶,从堆中移走,并将剩下的记录再调整成堆,找 出其中最大的记录,再移走,以此类推。 ..时间复杂度: 最好、最坏、平均都是。 (log)Onn 4.归并排序 归并排序: 将有序序列进行合并。 ..二路归并排序: 将若干个有序序列两两归并,直至所有待排序记录都在 一个有序序列为止。 ..时间复杂度: 最好、最坏、平均都是。 (log)Onn 2.7.2插入排序 ..直接插入排序 把数组A[n]中待排序的n个元素看成一个有序表和一个无序表,开始时, 有序表中只包含一个元素A[0],无序表中包含n-1个元素A[1]~A[n-1],排序的 过程是每次从无序表中取出一个元素,把它插入到有序表中的适当位置,使之成 为一个新的有序表。 这样经n-1次插入后,无序表变成空表,有序表就包含了全 部n个元素,排序完毕。 技巧: 在排序的过程中: 将元素的比较和移动结合在一起。 voidinsertsort(int*a,intn) { intj;intx; for(inti=1;i<=n-1;i++) { x=a[i]; for(j=i-1;j>=0;j--) { if(a[j]>x) a[j+1]=a[j]; else break; } a[j+1]=x;//注意这句不能与break放在一起 } } ..希尔排序: 首先以的()为步长,把数组A中的个元素分为个组,使 下标距离为的元素同在一个组中,即A[0],A[d1],A[2d1]…的n个元素为第 一组,A[1],A[d1+1],A[2d1+1]…为第二组,…,接着在每一个组内进行直接 插入排序,然后以 1d10dn<<.n1d1d2d()21dd<为步长,在上一步的基础上,把A中的n个元素重 新分成d个组,使下标为的元素在同一组中,接着再在每个组内进行直接插入 排序,依次类推,直到dt=1。 d voidshellsort(int*a,intn) { intx,i,j,d; for(d=n/2;d>=1;d/=2)//步长,一般选为质数 { for(i=d;i
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构