严蔚敏数据结构中p29 status ListInsert_L(LinkList &L,int L,ElemType e),如何理解LinkList &L?

2024-05-14

1. 严蔚敏数据结构中p29 status ListInsert_L(LinkList &L,int L,ElemType e),如何理解LinkList &L?

要搞清引用的作用,那是指向结构体的指针,用引用使得链表各节点形成联系,好好想想吧

严蔚敏数据结构中p29 status ListInsert_L(LinkList &L,int L,ElemType e),如何理解LinkList &L?

2. 数据结构 C语言 单链表 Status ListInsert_L(Linklist &L,int i,ElemType e) &L是什么意

在C语言中,函数形参是没有&修饰符的,这个&来自于C++,因为使用方便,且目前的大部分编译环境都支持C++,所以不讲究的人在C中使用了C++的&修饰符,在C++的函数形参表中,&修饰符表示对实参的引用,可以这样理解,通过&操作符,在主调函数和被调函数中,主调函数中的实参对于被调函数如同该函数局部变量一样的使用权限,就像全局变量一样。

3. 严蔚敏的数据结构与算法(c语言版) 中关于引用

引用常常是有必要的,他十分重要的一点就是可以避免对象的复制!

同样是传递,如果你要传递的是一个非常庞大的对象,这时候不用引用的话,传给函数的就是这个对象的一个副本,构造这样一个副本会调用拷贝构造函数,如果对象庞大,可能会花费大量的系统资源,所以引用的有意义的。而用了引用,就可以避免这样一个拷贝过程。举例,MFC中大量使用的就是常引用,因为他的类往往是一个控件或者是一个对话框。

如果为了避免或者强调传递的对象不发生改变,可以用“常引用”
就是const type &name的形式,这样既可以避免修改也可以避免拷贝。

用linklist l,这个子程序里面的l是外面传值传过来的一个linklist的拷贝,要做一次复制操作,会花费一定系统开销!
用linklist &l,不需要这个复制操作,节省资源!
这里并不关心改不改变l的值,使用引用不只是为了让l的值能改变,还有我说的这个作用

严蔚敏的数据结构与算法(c语言版) 中关于引用

4. 严书数据结构P31算法2.12MergeList-L中 pc->next=pa?pa:pb怎么理解?

while(pa&&pb){...} 表明两个链表都非空,此时根据data的大小来选择。
循环结束后有一个链表已经空了,pc->next=pa?pa:pb;这句话是把剩下非空的那个接到pc后面。

5. 关于数据结构的问题,算法如何运行

这是C  的强项。用C就比较费事了。   
    
    
  以quicksort为例。   
  你将不得不传入一个void   *   指向的数组[为了通用],传入数组的大小,传入每个元素的大小,以及一个比较大小的函数。   
  void   quicksort(void   *array,   size_t   arry_size,   size_t   element_size,   
            int   (*compare)(void   *   element1,   void   *   element2)   );   
    
  这个对于自定义的类型也就罢了,其效率损失可以接受,但对于内建类型,就太可怕了。   
    
  比如如果你有一个   int数组要排序,你要提供一个这样的int_compare   
  int   int_compare(void   *   element1,   void   *   element2)   
  {   
              return   *(int*)element1   <   *(int*)element2;   
  }   
    
  这里面执行的有意义的操作完全应该嵌入quicksort函数体。但是上面的quicksort要求一个函数指针,所以只能作为一般函数。这样调用过程中压栈出栈的费用数倍于有用操作。   
    
  另外,对于刚才讲的int数组,element_size是已知的,但是没办法,一定要传入,这是C中为了算法通用性必须付出的代价。这样每调用要多传2个参数[element_size,   int_compare],   而quicksort是递归函数,意味着有一个较大的乘数---C为了算法通用性付出的时间和栈空间上的额外代价。   
    
  这只是冰山一角,上面的算法认定传入的是一个数组,即各元素是顺序相连的。有些算法并不要求这种所谓的随机访问特性,可能只要求能顺序访问各元素,比如求和,对链表的元素求和和对数组元素求和都是O(N),就算法而言没有差别,如果你要实现这个算法,你必须这么写:   
  typedef   union{   
            int   int_value;   
            unsigned   ui_value;   
            long   long_value;   
            unsigned   long   ul_value;   
            float             float_value;   
            double           doubel_value;   
  }sum_type   ;   
    
  sum_type   sum(void   *   start,   void   *   end,   void   *(*next)(void   *),           sum_type   (*add)(sum_type,sum_type)   );   
    
  对一个保存整型值的链表,你可能这样实现其next和add   
  struct   i_node{   
          int   value;   
          struct   i_node   *   next;   
  };   
    
  //   current   不能为NULL   
  void   *i_list_next(void   *   current)   
  {   
            return   (struct   i_node   *)current->next;   
  }   
    
  sum_type   int_add(sum_type   op1,   sum_type   op2)   
  {   
            sum_type   result;   
            result.int_value   =   op1.int_value op2.int_value;   
  }   
    
  这样也能工作,可是如果你要加复数,怎么办?   你的sum_type已经不支持了。   
    
  C很好,高效,简洁。但是如果想要通用,还是用C  吧。STL让人惊艳。

关于数据结构的问题,算法如何运行

6. 数据结构,链表LinkList *L与LinkList L和Lnode

LNode是指你定义的结点类型,就是大括号中的包含指针域和数值域的变量。*LinkList是指你所定义的是一个链表不是单个结点。。LinkListL;L=malloc(sizeof(LinkList));是指申请一个链表的头结点的空间,并使该链表的指针指向该结点。。。因为理论上说链表空间可以无限,即整个内存的空间都可以为其所用,所以无需提前指定链表空间大小也可以继续申请下一个结点的空间。。LinkListL;是指定义一个链表。L=malloc(sizeof(LinkList));是指为该链表申请头结点,并使该链表的指针指向该结点LNode*p;p=malloc(sizeof(LNode));与上句不同,这句是指你申请一个结点的空间。LNode*p;是先定义一个结点。p=malloc(sizeof(LNode));是指为该结点申请内存空间。。注意一个结点和一个链表的区别就可以了。。

7. 数据结构作业

试描述头指针、头结点、开始结点的区别,并说明头指针和头结点的作用。
头指针:存放链表首地址的指针变量。头结点:链表的开始结点之前的一个同类型结点。开始结点:链表的第一个元素所在的结点。
头指针的作用:用于确定链表的地址。头结点的作用:方便于处理开始结点的操作和处理其它结点的操作保持一致,也方便于处理空表的操作和处理非空表的操作保持一致。
2.2
有哪些链表可由一个尾指针来唯一确定?即从尾指针出发能访问链表上任何一个结点。
单循环链表,双链表,双循环链表
★2.3
设线性表存放在向量A[arrsize]的前elenum个分量中,且递增有序。试写一算法,将x插入到线性表的适当位置上,以保持线性表的有序性。并且分析算法的时间复杂度。
#define
arrsize
100
int
InsertOrder(int
A[],
int
elenum,
int
x)
{
int
i=elenum-1;
if
(elenum==arrsize)
//在顺序表上进行插入操作必须先判满
{
printf(“full”);
return
0;
}
while
(i>=0&&A[i]>x)
{
A[i+1]=A[i];
i--;
}
//从后往前进行比较,比较的同时完成移动
A[i+1]=x;
elenum++;
return
elenum;
//返回变化之后的表长
}
//本题也可以先进行比较,比较的结果就是找到了插入的合适位置,然后再完成插入操作。但这样做比较耗时。
假设n=elenum,则时间复杂度:最坏O(n),最好O(1),平均O(n)
★2.4
用向量作存储结构,试设计一个算法,仅用一个辅助结点,实现将线性表中的结点循环右移k位的运算,并且分析算法的时间复杂度。
void
MoveKList(int
a[],int
n,int
k)
{
int
i,
j,
temp;
for
(i=1;
i<=k;
i++)
//外层for循环控制循环右移的次数i
{
temp=a[n-1];
//把表尾元素保存到辅助结点变量temp中
for
(j=n-2;
j>=0;
j--)
a[j+1]=a[j];
//内层for循环完成一次整体右移一位
a[0]=temp;
//把原来的表尾元素移至表头
}
}
时间复杂度T(n)
=
k*n
=
O(n)
★2.5
已知带头结点的动态单链表L中的结点是按整数值递增排列的,试写一算法将值为x的结点插入表L中,使L仍然有序。
typedef
int
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*next;
}
linklist;
//linklist结构体类型描述
void
InsertListOrder(linklist
*L,
datetype
x)
{
linklist
*p=L;
//对寻位指针p初始化
linklist
*s=(linklist
*)malloc(sizeof(linklist));
//使用强制类型转换将新结点的地址赋给指针s
s->data=x;
while((p->next)&&(p->next->data<x))
p=p->next;
//后移寻位指针
s->next=p->next;
p->next=s;
}
//本题也可以采用两个寻位指针p和q,让q始终跟随p的后移而后移。
★2.6
设计一算法,逆置带头结点的动态单链表L。
typedef
int
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*next;
}
linklist;
void
Reverse(linklist
*L)
{
linklist
*p,*q;
p=L->next;
q=L->next;
L->next=NULL;
while(q)
{
q=q->next;
p->next=L->next;
L->next=p;
p=q;
}
}
//用指针q遍历结点,指针p跟随指针q,使用头插法把当前结点*p插入到修改之后的单链表中。
2.7
试编写在带头结点的动态单链表和静态单链表上实现线性表操作Length(L)的算法,并将长度写入头结点的数据域中。
★(1)
typedef
int
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*next;
}
linklist;
void
Length1(linklist
*L)
{
linklist
*p=L-next;
int
i=0;
while(p)
{
i++;
p=p->next;
}
L->data=i;
//按照题目要求,将表长写入头结点的数据域中。
}
(2)
#define
maxsize
1024
typedef
int
datatype;
typedef
struct
{
datatype
data;
int
next;
}
node;
node
nodepool[maxsize];
void
Length2(int
L)
{
int
i=0,
p=nodepool[L].next;
while(p)
{
i++;
p=nodepool[p].next;
}
nodepool[L].data=i;
}
2.8
假设有两个按元素值递增有序排列的线性表A和B,均以单链表①作存储结构,试编写算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许值相同)排列的线性表C,并要求利用原表(即A表和B表)的结点空间存放表C。①今后若不特别指明,链表均是指动态链表,且可以带头结点。
typedef
int
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*next;
}
linklist;
linklist
*Connect
(
linklist
*A,
linklist
*B
)
{
linklist
*C,
*p,
*q,
*r;
C=A;
//C为最后返回的链表头指针
p=A->next;
//p总是指向链表A中当前正在比较的结点
q=B->next;
//q总是指向链表B中当前正在比较的结点
C->next=NULL;
//置空链表C
while(p&&q)
//当链表A和链表B中还有没比较的结点时
{
if
(p->datadata)
{
r=p;
p=p->next;
}
else
{
r=q;
q=q->next;
}
//r总是指向*p和*q二者中数据较小的结点
r->next=C->next;
C->next=r;
//将*r按照头插法插入到链表C中
}
if(!p)
//如果链表A中所有结点都链接到链表C中后,链表B中还有结点,
while(q)
//将链表B中剩余的未比较过的结点全部按照头插法插入到链表C中
{
r=q;
q=q->next;
r->next=C->next;
C->next=r;
}
else
//如果链表B中所有结点都链接到链表C中后,链表A中还有结点,
while(p)
//将链表A中剩余的未比较过的结点全部按照头插法插入到链表C中
{
r=p;
p=p->next;
r->next=C->next;
C->next=r;
}
free(B);
//释放链表B的头结点
return
C;
}
2.9
假设在长度大于1的单循环链表中,既无头结点也无头指针。s为指向链表中某个结点的指针,试编写算法删除结点*s的直接前趋结点。
typedef
int
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*next;
}
linklist;
void
DeleteBefore
(
linklist
*s
)
{
linklist
*p=s;
while(p->next->next!=s)
p=p->next;
free(p->next);
p->next=s;
}
2.10
已知,由单链表表示的线性表中,含有三类字符的数据元素(如:字母字符、数字字符和其它字符),试编写算法构造三个以循环链表表示的线性表,使每个表中只含同一类的字符,且利用原表中的结点空间作为这三个表的结点空间,头结点可另辟空间。
typedef
char
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*next;
}
linklist;
//L为等待分解的单链表,最后得到的包含字母字符的链表的首地址作为该函数的返回值被返回,得到的包含数字字符的链表的首地址被参数*LB带回,得到的包含其它字符的链表的首地址被参数*LC带回。
linklist
*
Decompose
(
linklist
*L,
linklist
**LB,
linklist
**LC
)
{
linklist
*A,
*B,
*C,
*pa,
*pb,
*pc,
*p;
//A,
B,
C分别用于保存分解之后得到的三个循环链表的首地址
//pa,
pb,
pc分别指向三个循环链表当前的尾结点
//p总是指向原单链表L中当前正在判断类型,正在等待处理的结点
A=L;
B=(linklist
*)malloc(sizeof(linklist));
C=(linklist
*)malloc(sizeof(linklist));
pa=A;
pb=B;
pc=C;
p=A->next;
while(p)
//只要p不为空,就意味着原单链表L中仍然有未处理的结点
{
if(((’a’data)&&(p->datadata)&&(p->data<=’Z’)))
{
pa->next=p;
pa=p;
p=p->next;
}
//将*p链接到链表A的终端,然后p后移
else
if((’0’data)&&(p->data<=’9’))
{
pb->next=p;
pb=p;
p=p->next;
}
//将*p链接到链表B的终端,然后p后移
else
{
pc->next=p;
pc=p;
p=p->next;
}
//将*p链接到链表C的终端,然后p后移
}
pa->next=A;
pb->next=B;
pc->next=C;
//让链表A、B、C都循环起来
*LB=B;
//通过指针类型的变量LB带回循环链表B的首地址
*LC=C;
//通过指针类型的变量LC带回循环链表C的首地址
return
A;
//通过函数返回值带回循环链表A的首地址
}
2.11
设有一个双链表,每个结点中除有prior、data和next三个域外,还有一个访问频度域freq,在链表被起用之前,其值均初始化为零。每当在链表进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度递减的顺序排列,以便使频繁访问的结点总是靠近表头。试编写实现符合上述要求的Locate运算的算法。
typedef
char
datatype;
typedef
struct
node
{
datatype
data;
struct
node
*prior,
*next;
int
freq;
}
dlinklist;
//创建符合题意的结点类型
dlinklist
*Locate(dlinklist
*L,
datatype
x)
{
dlinklist
*p=L->next;
//指针p用于查找第一个data域等于x的结点
dlinklist
*q;
while(p&&(p->data!=x))
p=p->next;
if(!p)
return
NULL;
//p为空,意味着没有找到data域等于x的结点
else
{
p->freq++;
//将找到的data域等于x的结点的访问频度值加1
q=p->prior;
//指针q用于查找在*p的前面结点中第一个freq域不小于当前p所指结点的freq域的结点
while((q!=L)&&(q->freq)freq))
q=q->prior;
if(q!=p->prior)
//如果q发生了前移,才有必要移动*p
{
p->prior->next=p->next;
if(p-next)
p->next->prior=p->prior;
//如果*p不是终端结点,才有必要修改*p的后继结点的prior域
p->prior=q;
p->next=q->next;
q->next=p;
p->next->prior=p;
//将*p插入到*q的后边
}
return
p;
}
}

数据结构作业

8. 数据结构题,用c++来完成。急求

#include 
#include 
/*函数状态码*/
#define TRUE 1  //成功
#define OK 1
#define FALSE 0 //失败 
#define ERROR 0 //错误 
#define INFEASIBLE -1   //不可行的
#define OVERFLOW -2     //溢出
#define LNODELENGTH sizeof(LNode)   //一个结点所占的空间
typedef int ElemType; //基本数据类型
typedef int Status; //函数的返回值类型 
/*单链表的存储结构*/
typedef struct LNode{
ElemType data;  //  数据域
struct LNode *next;     //指向下一个结点的指针
}LNode, *LinkList;
//初始化线性表L
Status ListInit_L(LinkList &L){
L = (LinkList)malloc(LNODELENGTH);      //分配大小为LNODELENGTH的空间 L指向空间的首地址
if (L == NULL){
printf("内存分配失败 初始化链表失败");
exit(INFEASIBLE);
}
L->data = 0;
L->next = NULL; 
return TRUE;
}
//销毁线性表L
Status DestroyList_L(LinkList &L){
LinkList p = L, q = p->next;
while (q != NULL){  //当p不是最后一个结点时
free(p);        //释放p指向的结点
p = q;  //p指向下一个要free的结点
q = p->next;
}   //循环结束后 p指向最后一个结点
free(p);        //释放最后一个结点的空间
return OK;
};
//将L置为空表
Status ClearList_L(LinkList L){
LinkList p;
while (L->next != NULL){    //当L->next不是NULL 即第二个结点存在时
p = L->next;    //p指向第二个结点
L->next = p->next;  //L的next指向第三个结点
free(p);    //释放第二个结点
}//循环结束时 表中只剩下头结点
return OK;
}
//若L为空表则返回TRUE 否则返回 FALSE
Status ListEmpty_L(LinkList L){
if (L->next == NULL)
return TRUE;
else
return FALSE;
}
//返回L中数据元素的个数
Status ListLength_L(LinkList L){
int sum = 0;    //用来存放数据元素的个数
LinkList p= L;
do{ //当p不是最后一个结点时
sum++;  //sum自增
p = p->next;
} while (p != NULL);
return sum;
}
//用e返回L中第i个数据元素的值
Status  GetElem_L(LinkList L, int i, ElemType * e){
if (iListLength_L(L)){  //当i小于0或i大于链表的长度时 无法查询第i个数据元素
return INFEASIBLE;
}
LinkList p = L;
int j=0;
while (j <= i){
p = p->next;
j++;
}
return p->data;
}
//返回 L中第一个与e满足关系compare()的数据元素的位序 若不存在 则返回0
Status LocateElem_L(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)){
LinkList p = L;
int i = 0;
while (!compare(p->data, e) && p->next != NULL){
p = p->next;
i++;
}   //退出循环有两种可能 一是找到了匹配的元素 二是没有找到 超出了线性表的长度
if (p->next == NULL){
i++;
if (compare(p->data, e))
return i;
else{
return FALSE;
}
}
else{
return i;
}
}
//若cur_e是L的数据元素 且不是第一个 则用pre_e返回它的前驱 否则操作失败 pre_e无定义
Status PriorElem_L(LinkList L, ElemType cur_e, ElemType * pre_e){
LinkList p = L;
if (cur_e == p->data){
printf("此元素没有前驱");
return FALSE;
}
while (p->next->data != cur_e || p == NULL){
p = p->next;
}
if (p == NULL){
printf("超出了线性表的长度\n");
return FALSE;
}
else{
*pre_e = p->data;
return TRUE;
}
}
///若cur_e是L的数据元素 且不是最后一个 则用next_e返回它的前驱 否则操作失败 next_e无定义
Status NextElem_L(LinkList L, ElemType cur_e, ElemType * next_e){
LinkList p = L;
while (p->data != cur_e || p == NULL){
p = p->next;
}
if (p == NULL){
printf("此数据元素没有后驱\n");
return FALSE;
}
else{
*next_e = p->next->data;
return TRUE;
}
}
//在L中的第i个位置之前插入新的数据元素e,L的长度加1
Status ListInsert_L(LinkList &L, int i, ElemType e){
LinkList p = L, newp;
int j=0, length = ListLength_L(L);
if (ilength+1){ //当i小于0或i大于链表的长度时 无法插入
printf("不可行\n");
return INFEASIBLE;
}
if (i == 1){    //头插
newp = (LinkList)malloc(LNODELENGTH);
if (newp == NULL){
printf("内存分配失败\n");
exit(INFEASIBLE);
}
newp->data = e;
newp->next = L;
return OK;
}
else if (i == length+1){    //尾插
while (p->next != NULL)
p = p->next;
newp = (LinkList)malloc(LNODELENGTH);
if (newp == NULL){
printf("内存分配失败\n");
exit(INFEASIBLE);
}
p->next = newp;
newp->data = e;
newp->next = NULL;
return OK;
}
else{
while (j < i-2){
j++;
p = p->next;
}//循环结束后  p为要插入位置的前一个结点
newp = (LinkList)malloc(LNODELENGTH);
if (newp == NULL){
printf("内存分配失败\n");
exit(INFEASIBLE);
}
newp->next = p->next;
newp->data = e;
p->next = newp;
return TRUE;
}
}
//删除L中的第i个元素 并用e返回其值 L的长度减1
Status ListDelete_L(LinkList &L, int i, ElemType * e){
LinkList p = L,q;
int j = 0, length = ListLength_L(L);
if (ilength + 1){   //当i小于0或i大于链表的长度时 无法插入
printf("不可行\n");
return INFEASIBLE;
}
while (j < i - 2){
j++;
p = p->next;
}//循环结束后  p为要删除位置的前一个结点
q = p->next;
p->next = q->next;
free(q);
return OK;
}
//对L的每一个元素调用函数visit 一旦visit()失败 则操作失败
Status ListTraverse_L(LinkList L, void(*visit)(ElemType *)){
LinkList p = L;
while (p != NULL){
visit(&(p->data));
p = p->next;
}
printf("\n");
return 0;
}
//若数据元素a与b相等则返回TRUE 否则返回 FALSE
Status compare(ElemType a, ElemType b){
if (a == b){
return TRUE;
}
else
return FALSE;
}
//输出数据元素
void visit(ElemType *e){
printf("%d ", *e);
}
//已知单链线性表La Lb的元素按值非递减排列
//归并La Lb得到新的单线性链表Lc Lc的元素也按值非递减排列
Status MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc){
LinkList pa = La, pb = Lb;
int i = 2;
if (La == NULL || Lb == NULL){
printf("空表无法合并");
return ERROR;
}
//La和Lb的头结点中最小的data赋给Lc头结点的data
if (pa->data data){
Lc->data = La->data;
pa = pa->next;
}
else{
Lc->data = Lb->data;
pb = pb->next;
}
while (pa != NULL || pb != NULL){
if (pa != NULL && pa->data data){
ListInsert_L(Lc, i, pa->data);
pa = pa->next;
}
else{
ListInsert_L(Lc, i, pb->data);
pb = pb->next;
}
i++;
}
return OK;
}
//求La和Lb的交集
Status IntersectList_L(LinkList La, LinkList Lb, LinkList Lc){
LinkList pa = La, pb = Lb;
if (pa == NULL || pb == NULL){
printf("空表无法求交集");
return ERROR;
}
int i = 1;
while (pb != NULL){
if (LocateElem_L(La,pb->data,compare)!=0){
if (i == 1) Lc->data = pb->data;
else
ListInsert_L(Lc, i, pb->data);
i++;
}
pb = pb->next;
}
return OK;
}
int main(){
LinkList La,Lb,Lc;
ListInit_L(La);
ListInit_L(Lb);
ListInit_L(Lc);
La->data = 3;
ListInsert_L(La, 2, 5);
ListInsert_L(La, 3, 8);
ListInsert_L(La, 4, 11);
Lb->data = 2;
ListInsert_L(Lb, 2, 4);
ListInsert_L(Lb, 3, 6);
ListInsert_L(Lb, 4, 8);
ListInsert_L(Lb, 5, 9);
ListInsert_L(Lb, 6, 11);
ListInsert_L(Lb, 7, 15);
ListInsert_L(Lb, 8, 20);
printf("链表La\n");
ListTraverse_L(La, visit);
printf("链表Lb\n");
ListTraverse_L(Lb, visit);
printf("La Lb的并集Lc\n");
MergeList_L(La, Lb, Lc);
ListTraverse_L(Lc,visit);
ClearList_L(Lc);        //将Lc置为空表
printf("La与Lb的交集Lc\n");
IntersectList_L(La, Lb, Lc);
ListTraverse_L(Lc, visit);
return 0;
}