单链表的第i个位置元素获取:
1.声明一个节点p,指向第一个结点,从j=1 开始遍历,并当j<i时继续
2.链尾时则不存在
3.成功则返回数据
status GetElem(LinkList L, int i, ElemType *e)
{
int j=1;
LinkList p=L->next;
while ( p && j<i )
{
p = p -> next;
j ++;
}
if ( !p || j>i )
{
return E;}
*e = p->next;
return ok;
}
再第i个位置插入s结点:
1.创建x新结点s,用malloc。s=(LinkList)malloc(sizeof(Node));
malloc: p = (type*)
malloc
(
sizeof
(type)) LinkList=Node *
2.不能直接到第i位置,需要从头遍历。
3.先获取到第i个结点p
4.s->next=p->next p->next=s
删除q结点
p->next =p->next->next(这没看懂程杰什么意思,为什么要p->next=q,q不应该本来就在的),
free(q)收回q
优点:对于插入或删除数据越频 繁的操作,单链衰的效率优势就越是明显。
创建单链表:
1.声明一结点p和计数器i;
2.初始化空链表L,L的头结点的指针指向NULL;
3.头插法for:生成一新结点幅值给p;p->=(*L)->next;(*L)->next=p
4.尾插法for: r->next=p;r=p r始终指向表尾。 r->next=NULL.
单链表整表删除
q=p->next;free(p);p=q不能直接删除,这样next就找不见了,
优缺点:
1.存储分配方式:顺序存储结构用一段连 续的存储单元依次存储 线性袤的数据元素。
单链表采用链式存储结 构,用干组任意的存储 单元存也钳主袤的元素。
2.查找:顺序O(1),单链表O(n)
插入和删除:顺序O(n),单链表O(1)
空间:顺序一开始要固定的大小。单链表不需要分配,个数不限
所以:当查找用的多时用顺序,插入删除用的多用单链表。还有元素个数。
其他链表结构:静态链表
用数组来代替指针。首先让数组元素都有两个数据域组成,data和cur。这种叫静态链表。
(感觉这个没什么用,有指针不需要这个)
循环链表:
将单链表中终结点的指针端由空指针指向头结点。
从任何一个结点出发可以访问全部链表。单链表的循环判断为p->next是否为空,现在为p->next不等于头结点。采用尾指针方便,此时头指针九尾s->next。
双向链表:
每个结点设置前驱结点*prior。
p\r插入s: s->prior、s->next、p->next、r->prior
p\s\r删除s: p->next=r、r->prior=p
第四章 栈与队列
栈stack:撤销、后退、历史倒退。
仅在栈顶进行插入push和删除pop操作。 栈底封闭固定。 后进先出。
是个线性表(有前驱和后继的关系)。
栈的顺序存储结构实现:
用数组0端作为栈底,栈顶为top。当栈只有一个元素时,top=0。当top=-1时,说明为空栈。定义为
typedef int SElemType; /* SElemType(为栈内元素的类型,这里是int) */
typedef struct
{
SElemType data[MAXSIZE];
int top; /* 栈顶指针 */
}SqStack;
Push: S->top++;S->data[S->top]=e; O(1)
POP: *e=s->data[s->top]; s->top--; O(1)
两栈共享空间:
栈1top1;栈2top2。合并成一块空间。两端是两个栈底。
top1:~n-1 top2:n-1~0。 top1+1=top2 则栈满。
栈的链式存储结构及实现
又叫链栈。 单链表头部和栈顶合二为一。此时不需要头结点。 基本不存在栈满情况。 空栈时top=NULL。
typedef struct StackNode /*链*/
{
SElemType data; /* SElemType为数据类型,这里为int */
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack /*栈*/
{
LinkStackPtr top;
int count;
}LinkStack;
进栈push:
status Push(LinkStack *S, SElemType e) /* status代表的是int,函数返回的也是int */
{
LinkStackPtr s=(LinkStackPtr)malloc(sizeof(LinkStcakPtr);
s->data=e; /* 开辟新结点s,放元素e,在把结点指针指向top */
s->next=s->top;
S->top=s;
S->count++;
return ok;
) O(1)
出栈POP:
*e=S->top->data; p=S->top; S->top=S->top->next; free(p); S->count--; O(1)
对比栈的顺序和链:
时间复杂度一样。空间:顺序需要固定长度,但存取时定位方便S->data[S->top];链栈长度无限制,但要放指针。
递归:函数自己调用自己
斐波那契数列:
int Fbi (int i)
{
if (i<2)
{ return i==0?0:1;}
return Fbi(i-1)+Fbi(i-2);
}
这种存储某些数据,并在后面又以存储的逆序恢复这些数据。使用栈。前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压入栈,在退回阶段,栈顶的东西被弹出,逐步返回代码的层次。
中后缀表达这块没看。感觉很奇怪
队列:
允许在一端 插入(队尾) 另一端 删除(队头6) 的线性表。先进先出。
应用广泛,实时输出类的,键盘、显示屏等。
队列的顺序存储:
front指针指向队尾(出队)
rear指针指向队尾(入队)
当front=rear时就空了,当不断加入的时候,rear不断往后,会出现假溢出。后面溢出了,前面还空着。 若采用循环队列。
rear可以指向0,但是rear会和front重合,其实是满了。但是如何判断。 满了。rear和front相差一个位置时就认为是满了:(rear+l) %QueueSlze ==front
循环队列长度:(Q. rear-Q.Xront+MAXSTZE) %MAXSIZE
入队:Q→data(Q->zear)=e 元素给队尾;Q->rear= (Q->rear+l) %MAXS工ZE;rear指针向后移一位
出队:*e=Q->data[Q->fron];Q->front- (Q->front+l) %MAXSIZE;front指针向后移一位,最后则转头部。 O(1)
队列的链式存储:
线性表的单链表,只能尾进头出。又叫链队列。
入队:先分配一个结点(用malloc),在给他s->data=e。s->next=NULL。原队尾rear的next为s,在把s设为队尾。
出队:头结点的后继结点出队,头结点的后继改为出队那个的next。若队头也是队尾,则需将rear=front。 O(1)
总结:
栈,表尾进行插入删除,类型相同时可以共享。队列:一端进一端出。
很高兴今天完成了目标,本来20分钟前想放弃的,但是0.99*0.99*0.99=很小的数。
明天看完第五章。