c语言第一版第三章答案,《数据结构与算法分析——c语言描述》第三章笔记与课后练习解答...

关于第三章:

第三章正式开始谈数据结构的实现,说的是最基本的链表、栈和队列。

链表的用处非常多,以后的其他数据结构(比如散列表、图)也会用到,非常基本而实用的数据结构。

而栈,则是编译器中递归实现的核心,学习这个数据结构可以有效的帮助理解递归的工作原理。

至于队列,经常用在模拟一些比如顾客排队时间,进程时间调度的问题上。

笔记:

抽象数据类型(ADT,abstract data type):

ADT是一些操作的集合。抽象数据类型是数学的抽象;

在ADT的定义中根本没有涉及如何实现操作的集合,也不存在什么法则来告诉我们必须有那些操作,这是一个设计决策——由程序员从相应的需求中决定自己需要的操作,并将操作实现。

表ADT:

数据以

51497a0395caf9da534f6af02d9ce0d0.png排列,将这类数据称为表,这个表的大小是N,大小为0的表称为空表。

除空表外,我们说

14f1c1828f0033a0870b1b0708773d9c.png后继

2cc0e9f88a3d66eee2a9f84fff0303f5.png,并称

7b63a37c719bd9f87f80f2791c968c1e.png前驱

2cc0e9f88a3d66eee2a9f84fff0303f5.png。我们不给

194bd374813c06f07974d16351c082c1.png定义前驱,也不给

5c525a1a5b5729a054a8fc006fe64c7c.png定义后继。

表的实现:

(1)数组实现:表的所有ADT都可以用数组实现,但是因为数组的连续储存方式,带来昂贵的插入和删除花费,所以我们一般不用简单数组作表的ADT实现。

(2)链表实现:由一些不在内存中相连的结构组成,每个结构包含指向下一结构的指针,称为Next指针。

头节点的使用:

因为处理

194bd374813c06f07974d16351c082c1.png时遇到的边界处理问题,为了简化链表实现,我们给链表使用头节点。

综上所述,一个带头节点的链表描述如下图:

237a93f17a1fb8be51024bb9977dcb8e.png

一个单链表的部分实现:

void Insert(List L,Position Prev,ElemType Input)

{

Position New;

New = CreateNode();

New->Data = Input;

New->Next = Prev->Next;

Prev->Next = New;

}

void Delete(List L,ElemType Search)

{

Position Prev;

Prev = FindPrev(L,Search);

if (Prev->Next != NULL) {

Position Target;

Target = Prev->Next;

Prev->Next = Target->Next;

free(Target);

}

}

Position Find(List L,ElemType Search)

{

Position Target;

Target = L->Next; // header assumed

while (Target != NULL &&

Target->Data != Search)

Target = Target->Next;

return Target;

}

Position FindPrev(List L,ElemType Search)

{

Position Prev;

Prev = L; // do not assumed header here!

while (Prev != NULL &&

Prev->Next->Data != Search)

Prev = Prev->Next;

return Prev;

}

注意对比Find()和FindPrev()函数对头节点的处理。

Find中,头节点内的无效元素会影响查找结果,所有在这里把头节点忽略掉。

而在FindPrev中,头节点可能作为某节点的前驱节点(比如查找目标是第一个有效节点的时候),所以在这里不忽略头节点。

List DeleteList(List L)

{

if (L->Next != NULL) {

Position Target,Temp;

Target = L->Next;

L->Next = NULL;

while (Target != NULL) {

Temp = Target->Next;

free(Target);

Target = Temp; // notice!

}

}

return L;

}

另外,编写DeleteList函数时,也要注意节点的处理,否则删除将不能正确进行。

双链表、循环链表:

7d092f9265450c8e4965910559067828.png

9a00880528b01e6defd943f98ca1a2f0.png

这两个链表的实现和单链表类似,只是节点的结构不同。

另外,要注意的是边界节点的处理,这里就不展开谈了。

struct db_list {

ElemType Data;

Pos Prev;

Pos Next;

};

游标(数组式链表)的实现:

游标是在不支持指针的语言中,使用链表的方法,它使用数组来模拟链表的运行,包括创建节点和删除节点。

void InitCursor(void)

{

int i;

for (i = 0; i < SpaceSize; i++)

Cursor[i].Next = i+1;

Cursor[i - 1].Next = 0;

}

List CreateList(void)

{

Position Header;

Header = Alloc();

Cursor[Header].Next = 0;

return Header;

}

首先,要初始化数组,制造首尾相连的效果。

然后,再编写两个函数实现数组中类似Alloc和Free的功能。

Position Alloc(void)

{

Position P;

P = Cursor[0].Next;

Cursor[0].Next = Cursor[P].Next;

return P;

}

void Free(Position P)

{

Cursor[0].Next = Cursor[P].Next;

Cursor[0].Next = P;

}

游标的各种操作和链表原理相同,要注意的是,在游标中的赋值,比如,注意不要这样:

P = P->Next

而是

P = Cursor[L].Next;

栈ADT:

栈是限制删除和插入只能在一个位置上进行的表,该位置叫做栈的顶。

栈又叫LIFO(后进现出,last in first out)表。

e5038692604b76b967b27b8680751264.png

栈的数组实现:

栈的数组实现比链表实现典型,这里只贴数组实现代码。

struct StackRecord {

int Size;

int Position;

ElemType * Data;

};

void Push(Stack S,ElemType Input)

{

if (!IsFull(S)) {

S->Data[++S->Position] = Input;

}

}

void Pop(Stack S)

{

if (!IsEmpty(S)) {

S->Position--;

}

}

注意Push中的

S->Data[++S->Position] = Input;

++一定要前置。

栈实现递归:

值得一提的是,在程序语言用,递归是使用栈实现的。

在下面的一段尾递归代码:

void Res(int num)

{

if (num == 0){

return;

}

else {

printf("%d\n",num);

Res(--num);

}

}

如果我们把5赋值给函数RecuresNum运行,则它的展开模型是这样的:

puts(5);

Res(4);

| |

|--puts(4); |

|--Res(3);

| |

|--puts(3); |

|--Res(2);

| |

|--puts(2); |

|--Res(1);

| |

|--puts(1); |

|--Res(0); |

| |

|------->----------|

(不会用linux下的画图软件,原谅我XD)

其中每个Res调用,都会将前一个Res函数的数据通过栈Push保存起来,等到新函数调用完毕,就原路通过Pop返回到原来的函数上。

队列ADT:

队列是插入在一端进行,而删除在另一端进行的表。

83a591be0c7dadcc5ecc7fa7df421010.png

同样的,队列的链表实现也相当直观,我们在这里只贴数组实现的代码。

struct QueueRecord {

int Capacity;

int Size;

Pos Front;

Pos Rear;

ElemType * Data;

};

void Enqueue(Queue Q,ElemType Input)

{

if (!IsFull(Q)) {

++Q->Size;

Q->Rear = Succ(Q,Q->Rear);

Q->Data[Q->Rear] = Input;

}

else {

fprintf(stderr,"Enqueue fail!");

}

}

void Dequeue(Queue Q)

{

if (!IsEmpty(Q)) {

--Q->Size;

Q->Front = Succ(Q,Q->Front);

}

else {

fprintf(stderr,"Dequeue fail!");

}

}

static Pos Succ(Queue Q,Pos Index)

{

if (++Index == Q->Capacity)

Index = 0;

return Index;

}

值得注意的是回绕的处理,其他的操作都很简单。

习题答案:

3.3 (a)

void SwapWithNext( Position BeforeP, List L )

{

Position P, AfterP;

P = BeforeP->Next;

AfterP = P->Next; /* Both P and AfterP assumed not NULL. */

P->Next = AfterP->Next;

BeforeP->Next = AfterP;

AfterP->Next = P;

}

3.4 链表实现交操作

List

Intersect( List L1, List L2 )

{

List Result;

Position L1Pos, L2Pos, ResultPos;

L1Pos = First( L1 ); L2Pos = First( L2 );

Result = MakeEmpty( NULL );

ResultPos = First( Result );

while( L1Pos != NULL && L2Pos != NULL )

{

if( L1Pos->Element < L2Pos->Element )

L1Pos = Next( L1Pos, L1 );

else if( L1Pos->Element > L2Pos->Element )

L2Pos = Next( L2Pos, L2 );

else

{

Insert( L1Pos->Element, Result, ResultPos );

L1 = Next( L1Pos, L1 ); L2 = Next( L2Pos, L2 );

ResultPos = Next( ResultPos, Result );

}

}

return Result;

}

3.5 链表实现并操作

List

Union( List L1, List L2 )

{

List Result;

ElementType InsertElement;

Position L1Pos, L2Pos, ResultPos;

L1Pos = First( L1 ); L2Pos = First( L2 );

Result = MakeEmpty( NULL );

ResultPos = First( Result );

while ( L1Pos != NULL && L2Pos != NULL ) {

if( L1Pos->Element < L2Pos->Element ) {

InsertElement = L1Pos->Element;

L1Pos = Next( L1Pos, L1 );

}

else if( L1Pos->Element > L2Pos->Element ) {

InsertElement = L2Pos->Element;

L2Pos = Next( L2Pos, L2 );

}

else {

InsertElement = L1Pos->Element;

L1Pos = Next( L1Pos, L1 ); L2Pos = Next( L2Pos, L2 );

}

Insert( InsertElement, Result, ResultPos );

ResultPos = Next( ResultPos, Result );

}

/* Flush out remaining list */

while( L1Pos != NULL ) {

Insert( L1Pos->Element, Result, ResultPos );

L1Pos = Next( L1Pos, L1 ); ResultPos = Next( ResultPos, Result );

}

while( L2Pos != NULL ) {

Insert( L2Pos->Element, Result, ResultPos );

L2Pos = Next( L2Pos, L2 ); ResultPos = Next( ResultPos, Result );

}

return Result;

}

注意3。4和3。5的区别。

3。12 不用递归翻转单链表

List

ReverseList( List L )

{

Position CurrentPos, NextPos, PreviousPos;

PreviousPos = NULL;

CurrentPos = L;

NextPos = L->Next;

while( NextPos != NULL )

{

CurrentPos->Next = PreviousPos;

PreviousPos = CurrentPos;

CurrentPos = NextPos;

NextPos = NextPos->Next;

}

CurrentPos->Next = PreviousPos;

return CurrentPos;

}

3。15 自调整表的链表实现

Position

Find( ElementType X, List L )

{

Position PrevPos, XPos;

PrevPos = FindPrevious( X, L );

if( PrevPos->Next != NULL ) /* Found. */

{

XPos = PrevPos ->Next;

PrevPos->Next = XPos->Next;

XPos->Next = L->Next;

L->Next = XPos;

return XPos;

}

else

return NULL;

}

3。24

因为只有49个活动记录,栈空间不会用完。但因为计算是指数上升的,所以计算不会在合理的时间内返回结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于您提到的《数据结构》一书,根据引用\[1\]和引用\[2\]的内容,这是一本关于数据结构的教材,作者是严蔚敏。该书的第一章是绪论,介绍了数据结构的基本概念和术语,以及算法和算法分析的内容。该书还包含习题集,其中第一章的习题包括算法设计题。此外,根据引用\[3\]的内容,书中还介绍了顺序栈的存储结构。至于是否有C语言版的第一版,我无法确定,因为引用的内容中没有提到具体的版本信息。如果您需要获取该书的C语言第一版,建议您查阅相关的图书出版信息或者在网上搜索相关资源。 #### 引用[.reference_title] - *1* [【数据结构】01-绪论《数据结构 C语言版(严蔚敏、吴伟民)》](https://blog.csdn.net/TMT123421/article/details/84568290)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [数据结构C语言严蔚敏版(第二版)超详细笔记附带课后习题](https://blog.csdn.net/chongyang_/article/details/109544719)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值