程序员数据结构笔记.docx
《程序员数据结构笔记.docx》由会员分享,可在线阅读,更多相关《程序员数据结构笔记.docx(26页珍藏版)》请在冰豆网上搜索。
程序员数据结构笔记
http:
//www.yuanma.org/data/2007/0614/article_2676.htm
数据结构
知识:
1.数据结构中对象的定义,存储的表示及操作的实现.
2.线性:
线性表、栈、队列、数组、字符串(广义表不考)
树:
二叉树
集合:
查找,排序
图(不考)
能力:
分析,解决问题的能力
过程:
●确定问题的数据。
●确定数据间的关系。
●确定存储结构(顺序-数组、链表-指针)
●确定算法
●编程
●算法评价(时间和空间复杂度,主要考时间复杂度)
一、数组
1、存放于一个连续的空间
2、一维~多维数组的地址计算方式
已知data[0][0]的内存地址,且已知一个元素所占内存空间S求data[i][j]在内存中的地址。
公式:
(add+(i*12+j)*S)(假设此数组为data[10][12])
注意:
起始地址不是data[0][0]时候的情况。
起始地址为data[-3][8]和情况;
3、顺序表的定义
存储表示及相关操作
4、顺序表操作中时间复杂度估计
5、字符串的定义(字符串就是线性表),存储表示
模式匹配算法(简单和KMP(不考))
6、特殊矩阵:
存储方法(压缩存储(按行,按列))
三对角:
存储于一维数组
三对角问题:
已知Aij能求出在一维数组中的下标k;已知下标k求Aij。
7、稀疏矩阵:
定义,存储方式:
三元组表、十字链表(属于图部分,不考)
算法
●数组中元素的原地逆置;对换
●在顺序表中搜索值为X的元素;
●在有序表中搜索值为X的元素;(折半查找)
●在顺序表中的第i个位置插入元素X;
●在顺序表中的第i个位置删除元素X;
●两个有序表的合并;算法?
线性表数据结构定义:
Typedefstruct{
intdata[max_size];
intlen;
}linear_list;
●模式匹配
●字符串相加
●求子串
●(i,j)<=>K注意:
不同矩阵所用的公式不同;
●稀疏矩阵的转置(两种方式,后种为妙)
●和数组有关的算法
例程:
求两个长整数之和。
a=130********
b=87081299
数组:
a[]:
13056952168
b[]:
87081299
由于以上的结构不够直观(一般越是直观越容易解决)将其改为:
a[]:
11 86125965031a[0]=11(位数)
b[]:
8 99218078000b[0]=8
c进位 01100111100
c[]:
11 76433044231c[0]的值(C位数)由c[max_s+1]决定!
注意:
在求C前应该将C(max_s+1)位赋0.否则为随机数;较小的整数高位赋0.
算法:
已知a,b两个长整数,结果:
c=a+b;
总共相加次数:
max_s=max(a[],b[])
程序:
for(i=1;i<=max_s;i++){
k=a[i]+b[i]+c[i];
c[i]=k%10;
c[i+1]=k/10;
}
求c位数:
if(c[max_s+1]==0)
c[0]=max_s;
else
c[0]=max_s+1;
以下代码是我编的(毕竟是初学者.不太简洁大家不要见怪!
):
/*两长整数相加*/
#include
#include
#definePRINprintf("\n");
intflag=0;/*a[0]>b[0]?
1:
0*/
/*max(a[],b[]){}*/
change(charda[],chardb[],inta[],intb[],intc[]){
inti;
if(a[0]>b[0]){
for(i=1;i<=a[0];a[i]=da[a[0]-i]-'0',i++);/*a[0]-'0'sogood!
*/
for(i=1;i<=b[0];b[i]=db[b[0]-i]-'0',i++);
for(i=b[0]+1;i<=a[0];b[i]=0,i++);
for(i=1;i<=a[0]+1;c[i]=0,i++);
flag=1;
}
else{
for(i=1;i<=b[0];b[i]=db[b[0]-i]-'0',i++);
for(i=1;i<=a[0];a[i]=da[a[0]-i]-'0',i++);
for(i=a[0]+1;i<=b[0];a[i]=0,i++);
for(i=1;i<=b[0]+1;c[i]=0,i++);
}
}
add(inta[],intb[],intc[]){
inti,sum;
if(flag==1){
for(i=1;i<=a[0];i++){
sum=a[i]+b[i]+c[i];
c[i+1]=sum/10;
c[i]=sum%10;
}
if(c[a[0]+1]==0)
c[0]=a[0];
else
c[0]=a[0]+1;
}
else{
for(i=1;i<=b[0];i++){
sum=a[i]+b[i]+c[i];
c[i+1]=sum/10;
c[i]=sum%10;
}
if(c[b[0]+1]==0)
c[0]=b[0];
else
c[0]=b[0]+1;
}
}
voidprint(intm[]){
inti;
for(i=m[0];i>=1;i--)
printf("%d,",m[i]);PRIN
}
main(){
ints;
inta[20],b[20],c[20];
charda[]={"123456789"};
chardb[]={"12344443"};
a[0]=strlen(da);
b[0]=strlen(db);
printf("a[0]=%d\t",a[0]);
printf("b[0]=%d",b[0]);PRIN
change(da,db,a,b,c);
printf("flag=%d\n",flag);PRIN
printf("-----------------\n");
if(flag==1){
print(a);PRIN
s=abs(a[0]-b[0]);
printf("+");
for(s=s*2-1;s>0;s--)
printf("");
print(b);PRIN
}
else{
s=abs(a[0]-b[0]);
printf("+");
for(s=s*2-1;s>0;s--)
printf("");
print(a);PRIN
print(b);PRIN
}
add(a,b,c);
printf("-----------------\n");
print(c);
}
时间复杂度计算:
●确定基本操作
●计算基本操作次数
●选择T(n)
●lim(F(n)/T(n))=c
●0(T(n))为时间复杂度
上例子的时间复杂度为O(max_s);
二:
链表
1、知识点
●逻辑次序与物理次序不一致存储方法;
●单链表的定义:
术语(头结点、头指针等)
●注意带头结点的单链表与不带头结点的单链表区别。
(程序员考试一般不考带头结点,因为稍难理解)
●插入、删除、遍历(p==NULL表明操作完成)等操作
●循环链表:
定义,存储表示,操作;
●双向链表:
定义,存储方法,操作;
单链表和循环链表区别在最后一个指针域值不同。
2、操作
●单链表:
插入X,删除X,查找X,计算结点个数
●单链表的逆置(中程曾考)
head->NULL/p->a1/p->a2/p->a3/p……an/NULL注:
p代表指针;NULL/p代表头结点
=》head->NULL/p->an/p->an-1/p->an-2/p……a1/NULL
●循环链表的操作:
插入X,删除X,查找X,计算结点个数;
用p=head->next来判断一次计算结点个数完成;
程序段如下:
k=0;
do{
k++;
p=p->next;
}while(p!
=head->next);
●双向链表
●多项式相加
●有序链表合并
例程:
已知两个字符串S,T,求S和T的最长公子串;
1、逻辑结构:
字符串
2、存储结构:
数组
3、算法:
精化(精细化工)**老顽童注:
此处“精细化工”说明好像不对!
s="abaabcacb"
t="abdcabcaabcda"
当循环到s.len-1时,有两种情况:
s="abaabcacb"、s="abaabcacb"
s.len-2时,有三种情况:
s="abaabcacb"、s="abaabcacb"、s="abaabcacb"
.
.
.
1s.len种情况
程序思路:
tag=0//没有找到
for(l=s.len;l>0&&!
tag;l--){
判断长度为l的s中的子串是否为t的子串;
若是:
tag=1;
}
长度为l的s的子串在s中有(s.len-l+1)个。
子串0:
0~l-1
1:
1~l
2:
2~l+1
3:
3~l+2
……
……
s.len-l:
s.len-l~s.len-1
由上面可得:
第j个子串为j~l+j-1。
判断长度为l的s中的子串是否为t的子串:
for(j=0;jtag;j++){
判断s中长度为l的第j个子串是否为t的子串;
如果是:
tag=1;
}
模式结构:
tag=0;
for(l=s.len;l>0&&tag==0;l--){
for(j=0;jtag;j++){
?
?
用模式匹配方法确定s[j]~s[l+j-1]这个字符串是否为t的子串;//好好想想
若是,tag=1;
}
}
第二天
转眼又过了一周了,前面一周里面我编了一些程序:
链表,长整型数相加,三元组表转置以及一些简单的函数.其实有些算法想想是很简单,不过写起来还是需要一定耐心和C基础的,如果你自己觉得各算法都很懂了,不妨开机编编试试.或许会有一些新的发现与体会.
栈和队列
1、知识点:
●栈的定义:
操作受限的线性表
●特点:
后进先出
●栈的存储结构:
顺序,链接
/push(s,d)
●栈的基本操作:
\pop(s)
栈定义:
struct{
datatypedata[max_num];
inttop;
};
●队列定义
特点:
先进先出
/入队列in_queue(Q,x)
●队列的操作:
\出队列del_queue(Q)
●队列存储结构:
链队列:
Typedefstructnode{
Datatypedata;
Structnode*next;
}NODE;
Typedefstruct{
NODE*front;
NODE*rear;
}Queue;
顺序队列:
struct{
datatypedata[max_num];
intfront,rear;
};
问题:
队列⇔线性表
假溢出<=循環队列
队列满,队列空条件一样<=浪费一个存储空间
递归
定义:
问题规模为N的解依赖于小规模问题的解。
问题的求解通过小规模问题的解得到。
包括二个步骤:
1)递推6!
=>5!
=>4!
=>3!
=>2!
=>1!
=>0!
2)回归720<=120<=24<=6<=2<=1<=0
递归工作栈实现递归的机制。
2、有关算法:
1)顺序,链表结构下的出栈,入栈
2)循環,队列的入队列,出队列。
3)链队列的入队列,出队列。
4)表达式计算:
后缀表达式35+6/4368/+*-
中缀表达式(3+5)/6-4*(3+6/8)
由于中缀比较难处理,计算机内一般先将中缀转换为后缀。
运算:
碰到操作数,不运算,碰到操符,运算其前两个操作数。
中缀=>后缀
5)迷宫问题
6)线性链表的递归算法一个链表=一个结点+一个链表
intfuction(NODE*p){
if(p==NULL)return0;
elsereturn(function(p->next));
}
树与二叉树
一、知识点:
1.树的定义:
data_struct(D,R);
其中:
D中有一个根,把D和出度去掉,可以分成M个部分.
D1,D2,D3,D4,D5…DM
R1,R2,R3,R4,R5…RM
而子树Ri形成树.
1)递归定义高度
2)结点个数=1
O
--0
O
O
--1
O
O
O
O
--2
此树的高度为2
2.二叉树定义:
结点个数>=0.
3.术语:
左右孩子,双亲,子树,度,高度等概念.
4.二叉树的性质
●层次为I的二叉树I层结点2I个
●高度为H的二叉树结点2H+1-1个
●H(点)=E(边)+1
●个数为N的完全二叉树高度为|_LOG2n_|
●完全二叉树结点编号:
从上到下,从左到右.
i结点的双亲:
|_i/2_|
|_i-1/2_|
1
i结点的左孩子:
2i
2i+1
2
3
i结点的右孩子:
2i+1
2i+2
4
5
6
7
(根)
1为起点
0为起点
二叉树的存储结构:
1)扩展成为完全二叉树,以一维数组存储。
A
B
C
D
E
F
G
H
I
数组下标
0
1
2
3
4
5
6
7
8
9
10
11
12
元素
A
B
C
D
E
F
G
H
I
2)双亲表示法
数组下标
0
1
2
3
4
5
6
7
8
元素
A
B
C
D
E
F
G
H
I
双亲
-1
0
0
1
2
2
3
3
4
3)双亲孩子表示法
数组下标
0
1
2
3
4
5
…
元素
A
B
C
D
E
F
…
双亲
-1
0
0
1
2
2
…
左子
1
3
4
…
右子
2
-1
5
…
结构:
typedefstruct{
datatypedata;
intparent;
intlchild;
intrchild;
}NODE;
NODEtree[N];//生成N个结点的树
4)二叉链表
5)三叉链表
6)哈夫曼树
5.二叉树的遍历
先根\
中根栈中根遍历(左子树)根(右子树),再用相同的方法处理左子树,右子树.
后根/
先,中序已知求树:
先序找根,中序找确定左右子树.
层次遍历(队列实现)
6.线索二叉树(穿线树)
中序线索二树树目的:
利用空指针直接得到中序遍历的结果.
手段(方法):
左指针为空,指向前趋,右指针为空,指向后继.
结点结构:
ltag
Lch
Data
rch
rtag
Ltag=0,lch指向左孩子,ltag=1,指向前趋结点
Rtag=0,rch指向右孩子;rtag=1,指向后继结点
中序遍历:
1)找最左结点(其左指针为空)
2)当该结点的rtag=1,该结点的rch指向的就为后继
3)当rtag=0,后继元素为右子树中最左边那个
N个结点的二树有空指针N+1个
周六我去了周SIR的办公室,他很热情,我问的是中序线索化二叉树的问题(递归),关于这个问题我会在以后的笔记中作重点补充。
我在这学校从来没有碰到过像他这样热情的老师,真的,大一的时候我们学校就开了C,当时我就连#include这句话的意思都不晓得,别说是让我写程序了(到这份上也不怕把丑事都抖出来了:
《数据结构》的课程设计也是哈科大的littlebob兄帮我做的,很遗憾,他高程就差几分,希望他早日成功,我们都要向他学习)等于说我的C知识九成都是在大二下学期的时候学的。
而且全是自学的。
拿这个周末来说吧。
我三天时间就看完了一本C语言大全。
当然,并不是从头到尾,只是根据自己的实际情况,重点是指针和数据结构那块。
C最要的便是指针。
程序员考试下午试题最重要的便是递归问题(1~2道,没有掌握就没希望了哦)。
我说这些并不是为了表明自己多么用功,只是希望每位"学者"都有所侧重。
第三天
排序查找是我自己觉得最头疼的算法了,常搞混去的啊.不知道各位学得如何,如果不错,还请告诉我一些经验!
查找
一、知识点 /静态查找->数组
1、什么是查找
\动态查找->链树
●顺序查找,时间复杂度O(n)
●折半查找:
条件:
有序;时间复杂度O(nlog2n)(时间复杂度实际上是查找树的高度)
●索引查找:
条件:
第I+1块的所有元素都大于第I块的所有元素。
算法:
根据index来确定X所在的块(i)时间复杂度:
m/2
在第I块里顺序查找X 时间复杂度:
n/2
总的时间复杂度:
(m+n)/2
●二叉排序树 1)定义:
左子树键值大于根节点键值;右子树键值小于根的键值,其左右子树均为二叉排序树。
2)特点:
中序遍历有序->(删除节点用到此性质)
3)二叉排序树的查找:
如果根大于要查找的树,则前左子树前进,如果根小于要查找的树,则向右子树前进。
4)结点的插入->二叉排序树的构造方法
5)结点删除(难点) 1、右子树放在左子树的最右边
2、左子树放在右子树的最左边
●avl树(二叉平衡树):
左右子树高度只能差1层,即|h|<=1其子树也一样。
●B树:
n阶B树满足以下条件 1)每个结点(除根外)包含有N~2N个关链字。
2)所有叶子节点都在同一层。
3)B树的所有子树也是一棵B树。
特点:
降低层次数,减少比较次数。
排序
一、知识点
1、排序的定义
/内排序:
只在内存中进行
2、排序的分类
\外排序:
与内外存进行排序
内排序:
/直接插入排序
1)插入排序
\shell排序
/冒泡排序
2)交换排序
\快速排序
/简单选择排序
3)选择排序堆
\锦标赛排序
4)归并排序(二路)
5)基数排序(多关链字排序)
3、时间复杂度(上午题目常考,不会求也得记住啊兄弟姐妹们!
)
******15***15***
/稳定 ********1515****(前后不变)
排序
\不稳定 ********1515****(前后改变)
经整理得:
选择、希尔、堆、快速排序是不稳定的;直接插入、冒泡、合并排序是稳定的。
●锦标赛排序方法:
13 16 11 18 21 3 17 6
\ / \ / \ / \ /
13 11 3 6
\ / \ /
11 3
\ /