关于数据结构课程里不同节点定义对程序实现的影响
来源:互联网 发布:千里走单骑知乎 编辑:程序博客网 时间:2024/05/17 18:23
近期在学习数据结构课程,主要学习的是武大李春葆教授的MOOC视频,同时也参考了严蔚敏版的教材和程杰的大话数据结构。发现李版对节点的定义与严版有所不同,进而影响到了编程实现的程序内容。下面将以链表的2种节点定义为例,剖析编程实现的不同。
先看严版和大话办的单链表节点定义:
typedef struct LNode
{
ElemType data;
struct LNode *next;
} LNode, *LinkList;
typedef struct LNode
{
ElemType data;
struct LNode *next;
} LinkList;
并没有直接将LinkList定义为节点指针,这样在程序中定义新节点指针时前面将带有*,比较直观。
下面是根据严版节点定义用C语言实现的单链表操作:
void CreateListF(LinkList *L, ElemType a[], int n) //头插法整体创建单链表,注意这里*L实质上是双重指针,即**LinkList. 下同。
{
LinkList s; //根据严版定义虽然这里没有*,但s 实际是指针,不妨称为隐式指针
int i;
(*L) = (LinkList)malloc(sizeof(LNode)); //创建头结点
if(!L)
exit(OVERFLOW);
(*L)->next = NULL; //头结点next域置为NULL
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList)malloc(sizeof(LNode));
s->data = a[i]; //将s插在头结点之后原开始节点之前
s->next = (*L)->next;
(*L)->next = s;
}
}
void CreateListR(LinkList *L, ElemType a[], int n) //尾插法整体创建单链表
{
LinkList s, r;
int i;
(*L) = (LinkList)malloc(sizeof(LNode)); //创建头结点
if(!(*L))
exit(OVERFLOW);
r= (*L); //r始终指向尾节点,开始时指向头结点
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList)malloc(sizeof(LNode));
s->data = a[i]; //将s插在r之后
r->next = s;
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void InitList(LinkList *L) //初始化线性表(单链表),即创建一个头结点
{
(*L) = (LinkList)malloc(sizeof(LNode));
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
(*L)->next = NULL;
}
void DestroyList(LinkList *L) //销毁线性表
{
LinkList pre=(*L), p=(*L)->next; //pre指向*p的前驱节点
while(p != NULL) //扫描单链表L
{
free(pre); //释放*pre节点
pre=p; //pre和p同步后移一个节点
p=pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向为节点,需要单独释放它
printf("Destroy success!\n");
}
bool ListEmpty(LinkList L) //判断是否为空表
{
return(L->next == NULL);
}
int ListLength(LinkList L) //求线性表长度,返回单链表L中的数据节点的个数
{
int n=0;
LinkList p=L; //p指向头结点,n置为0,即头结点的序号为0
while(p->next != NULL)
{
n++;
p = p->next;
}
return(n); //循环结束,p指向为节点,其序号n即为节点个数
}
void DispList(LinkList L) //输出线性表
{
LinkList p = L->next; //p指向开始节点
while(p != NULL) //p不为NULL,输出*p节点的data域
{
printf("%c ", p->data);
p = p->next; //移向下一个节点
}
printf("\n");
}
bool GetElem(LinkList L, int i, ElemType *e) //返回线性表中第i个元素的值给e
{
int j=0;
LinkList p=L; //p指向头结点,j置为0,即头结点的序号为0
while(j<i && p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
{
printf("The %d data no exist!\n", i); //第i个数据节点不存在,返回false
return false;
}
else
{
*e=p->data; //存在第i个数据节点,返回true
return true;
}
}
int LocateElem(LinkList L, ElemType e) //查找第1个值域与e相等的元素的逻辑位序,不存在则返回值为0
{
int i=1;
LinkList p=L->next; //p指向开始节点,i置为1
while(p!=NULL && p->data!=e) //查找data值为e的节点,其序号为i
{
p=p->next;
i++;
}
i++;
if(p==NULL)
return 0;
else
return i;
} //算法时间复杂度为O(n),不具有随机存取特性
bool ListInsert(LinkList *L, int i, ElemType e) //在单链表逻辑序号的第i个位置插入新的元素e
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点,将值为e的节点插入其后
LinkList p=(*L), s; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点*p,插入新节点并返回true
{
s=(LinkList)malloc(sizeof(LNode)); //创建新节点*s,其data域置为e
s->data=e;
s->next=p->next; //s插入到p之后
p->next=s;
return true;
}
}
bool ListDelete(LinkList *L, int i, ElemType *e) //删除单链表逻辑序号的第i个元素
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点并且后继节点也存在,则删除该后继节点
LinkList p=(*L), q; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点
{
q=p->next; //q指向第i个节点
if(q==NULL) //若不存在第i个节点,返回false
return false;
*e=q->data;
p->next=q->next; //从单链表中删除*q节点并释放
free(q);
return true;
}
}
void delmaxnode(LinkList *L) //删除单链表L中元素值最大的节点(假定最大值节点唯一)
{
LinkList p, pre, maxp, maxpre; //pre p是一对同步指针
p=(*L)->next;
pre=(*L);
maxp=p;
maxpre=pre; //maxpre和maxp用于同步记录pre p
while(p!=NULL)
{
if(maxp->data < p->data) //若找到更大节点则更改maxp和maxpre
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p->next; //p和pre同步后移
}
maxpre->next = maxp->next; //删除*maxp节点
free(maxp);
}
void sort(LinkList *L) //带头结点的单链表L(至少一个数据节点),本算法使其递增有序排列
{
LinkList p, pre, q; //思路:将L拆分为2部分,第一部分是头结点加第一个数据节点形成有序单链表
p = (*L)->next->next; //第二部分是从原第2个数据节点开始的剩余全部数据节点
(*L)->next->next = NULL;
while(p != NULL) //q用于保存*p的后继结点的指针
{
q = p->next;
pre = (*L);
while(pre->next != NULL && //从有序表头进行比较,查找pre,在其之后插入*p节点
pre->next->data < p->data)
pre = pre->next;
p->next = pre->next; //继续扫描第二部分余下的节点
pre->next = p;
p = q;
}
} //该算法时间复杂度为O(n^2)
void Reverse(LinkList *L) //将一个带头结点的单链表所有节点逆置
{
LinkList p = (*L)->next, q;
(*L)->next = NULL;
while(p != NULL) //头插法
{
q = p->next;
p->next = (*L)->next;
(*L)->next = p;
p = q;
}
}
void split(LinkList *L, LinkList *L1, LinkList *L2)
{
LinkList p = (*L)->next, q, r1;
(*L1) = (*L);
r1 = *L1;
(*L2) = (LinkList)malloc(sizeof(LNode));
(*L2)->next = NULL;
while(p != NULL)
{
r1->next = p;
r1 = p;
p = p->next;
q = p->next;
p->next = (*L2)->next;
(*L2)->next = p;
p = q;
}
r1->next = NULL;
}
/*这是基于严版定义只用C语言实现的单链表算法的主程序(使用了部分基本操作函数) */
#include <stdio.h>
#include<stdlib.h>
#include <stdbool.h>
#include <math.h>
typedef char ElemType;
#define OVERFLOW -1
#include "LinkList Lnode.h"
#include "LinkList basic operation.cpp"
int main()
{
int n;
char a[]={"aAbBcCdDeEfF"};
LinkList L, L1, L2; //根据严版定义虽然这里没有*,但L, L1, L2 实际是指针
CreateListF(&L, a, 12); //这里&是取地址
printf("The original LinkList:\n");
DispList(L);
printf("The reverse LinkList:\n");
Reverse(&L);
DispList(L);
split(&L, &L1, &L2);
printf("The L1 linklist:\n");
DispList(L1);
printf("The L2 linklist:\n");
DispList(L2);
n = ListLength(L2);
printf("n=%d\n", n);
DestroyList(&L2);
return 0;
}
李春葆老师在MOOC视频里特地强调了输出型参数引用的使用,将引用符号"&"放在形参的前面。
由于C语言编译器不支持引用参数 ,所以要用C++编译器编译程序,尽管程序基本全是C语言写成的。
通过下面的程序也可以看出基于李版节点定义编程时,该定义非常适合引用参数的使用,比C语言使用
指针更方便。下面是相对应的具体操作函数实现和主程序(C++编译器):
void CreateListF(LinkList *&A, ElemType a[], int n) //头插法整体创建单链表,注意*&A,表示引用参数L. 下同.
{
LinkList *s;
int i;
A = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!A)
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
A->next = NULL; //头结点next域置为NULL
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在头结点之后原开始节点之前
s->next = A->next;
A->next = s;
}
}
void CreateListR(LinkList *&L, ElemType a[], int n) //尾插法整体创建单链表,链表的节点顺序与逻辑次序相同
{
LinkList *s, *r;
int i;
L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!L)
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
r = L; //r始终指向尾节点,开始时指向头结点
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在r之后
r->next = s;
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void InitList(LinkList *&L) //初始化线性表(单链表),即创建一个头结点
{
L = (LinkList*)malloc(sizeof(LinkList));
if(!L)
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
L->next = NULL;
}
void DestroyList(LinkList *&L) //销毁线性表
{
LinkList *pre=L, *p=L->next; //pre指向*p的前驱节点
while(p != NULL) //扫描单链表L
{
free(pre); //释放*pre节点
pre=p; //pre和p同步后移一个节点
p=pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向为节点,需要单独释放它
}
bool ListEmpty(LinkList *L) //判断是否为空表
{
return(L->next == NULL);
}
int ListLength(LinkList *L) //求线性表长度,返回单链表L中的数据节点的个数
{
int n=0;
LinkList *p=L; //p指向头结点,n置为0,即头结点的序号为0
while(p->next != NULL)
{
n++;
p = p->next;
}
return(n); //循环结束,p指向为节点,其序号n即为节点个数
}
void DispList(LinkList *L) //输出线性表
{
LinkList *p = L->next; //p指向开始节点
while(p != NULL) //p不为NULL,输出*p节点的data域
{
printf("%c ", p->data);
p = p->next; //移向下一个节点
}
printf("\n");
}
bool GetElem(LinkList *L, int i, ElemType &e) //返回线性表中第i个元素的值给e
{
int j=0;
LinkList *p=L; //p指向头结点,j置为0,即头结点的序号为0
while(j<i && p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
{
printf("The %d data no exist!\n", i); //第i个数据节点不存在,返回false
return false;
}
else
{
e=p->data; //存在第i个数据节点,返回true
return true;
}
}
int LocateElem(LinkList *L, ElemType e) //查找第1个值域与e相等的元素的逻辑位序,不存在则返回值为0
{
int i=1;
LinkList *p=L->next; //p指向开始节点,i置为1
while(p!=NULL && p->data!=e) //查找data值为e的节点,其序号为i
{
p=p->next;
i++;
}
i++;
if(p==NULL)
return 0;
else
return i;
} //算法时间复杂度为O(n),不具有随机存取特性
bool ListInsert(LinkList *&L, int i, ElemType e) //在单链表逻辑序号的第i个位置插入新的元素e
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点,将值为e的节点插入其后
LinkList *p=L, *s; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点*p,插入新节点并返回true
{
s=(LinkList*)malloc(sizeof(LinkList)); //创建新节点*s,其data域置为e
s->data=e;
s->next=p->next; //s插入到p之后
p->next=s;
return true;
}
}
bool ListDelete(LinkList *&L, int i, ElemType &e) //删除单链表逻辑序号的第i个元素
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点并且后继节点也存在,则删除该后继节点
LinkList *p=L, *q; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点
{
q=p->next; //q指向第i个节点
if(q==NULL) //若不存在第i个节点,返回false
return false;
e=q->data;
p->next=q->next; //从单链表中删除*q节点并释放
free(q);
return true;
}
}
void delmaxnode(LinkList *&L) //删除单链表L中元素值最大的节点(假定最大值节点唯一)
{
LinkList *p, *pre, *maxp, *maxpre; //pre p是一对同步指针
p=L->next;
pre=L;
maxp=p;
maxpre=pre; //maxpre和maxp用于同步记录pre p
while(p!=NULL)
{
if(maxp->data < p->data) //若找到更大节点则更改maxp和maxpre
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p->next; //p和pre同步后移
}
maxpre->next = maxp->next; //删除*maxp节点
free(maxp);
}
void sort(LinkList *&L) //带头结点的单链表L(至少一个数据节点),本算法使其递增有序排列
{
LinkList *p, *pre, *q; //思路:将L拆分为2部分,第一部分是头结点加第一个数据节点形成有序单链表
p = L->next->next; //第二部分是从原第2个数据节点开始的剩余全部数据节点
L->next->next = NULL;
while(p != NULL) //q用于保存*p的后继结点的指针
{
q = p->next;
pre = L;
while(pre->next != NULL && //从有序表头进行比较,查找pre,在其之后插入*p节点
pre->next->data < p->data)
pre = pre->next;
p->next = pre->next; //继续扫描第二部分余下的节点
pre->next = p;
p = q;
}
} //该算法时间复杂度为O(n^2)
void Reverse(LinkList *&L) //将一个带头结点的单链表所有节点逆置
{
LinkList *p = L->next, *q;
L->next = NULL;
while(p != NULL) //头插法
{
q = p->next;
p->next = L->next;
L->next = p;
p = q;
}
}
void split(LinkList *&L, LinkList *&L1, LinkList *&L2)
{
LinkList *p = L->next, *q, *r1;
L1 = L;
r1 = L1;
L2 = (LinkList*)malloc(sizeof(LinkList));
L2->next = NULL;
while(p != NULL)
{
r1->next = p;
r1 = p;
p = p->next;
q = p->next;
p->next = L2->next;
L2->next = p;
p = q;
}
r1->next = NULL;
}
#include "Headfile.h"
typedef char ElemType;
#include "LinkList Lnode.h"
#include "LinkList basic operation.cpp"
//typedef char ElemType;
int main()
{
char a[]={"aAbBcCdDeEfF"};
LinkList *L, *L1, *L2; //这里有*,一目了然知道是指针
CreateListF(L, a, 12); //函数调用时无需取地址符&了
printf("The original LinkList:\n");
DispList(L);
printf("The reverse LinkList:\n");
Reverse(L);
DispList(L);
split(L, L1, L2);
printf("The L1 linklist:\n");
DispList(L1);
printf("The L2 linklist:\n");
DispList(L2);
DestroyList(L2);
int n = ListLength(L2);
return 0;
}
如果强调一定要用C编译器的情况,基于李版定义也能实现,显式的使用了双重指针;
而严版定义程序其实是隐式的使用了双重指针。下面是C编译器下李版定义程序:
/*C编译器下基于李版节点定义的操作函数和主程序 */
void CreateListF(LinkList **L, ElemType a[], int n) //头插法整体创建单链表,注意这里L前面是2个*,即指针的指针
{ //这样就可以改变主函数传递过来的内容并返回!
LinkList *s; //显示指针
int i;
*L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
(*L)->next = NULL; //头结点next域置为NULL
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在头结点之后原开始节点之前
s->next = (*L)->next;
(*L)->next = s;
}
printf("子函数*L创建后的的地址 %p\n",*L);
printf("子函数L创建后的地址 %p\n",L);
}
void DispList(LinkList *L) //输出线性表
{
LinkList *p = L->next; //p指向开始节点
while(p != NULL) //p不为NULL,输出*p节点的data域
{
printf("%c ", p->data);
p = p->next; //移向下一个节点
}
printf("到这里是正确的!\n");
}
void CreateListR(LinkList **L, ElemType a[], int n) //尾插法整体创建单链表,链表的节点顺序与逻辑次序相同
{
LinkList *s, *r;
int i;
*L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
r = (*L); //r始终指向尾节点,开始时指向头结点
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在r之后
r->next = s;
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void InitList(LinkList **L) //初始化线性表(单链表),即创建一个头结点
{
*L = (LinkList*)malloc(sizeof(LinkList));
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
(*L)->next = NULL;
}
void DestroyList(LinkList **L) //销毁线性表
{
LinkList *pre=*L, *p=(*L)->next; //pre指向*p的前驱节点
while(p != NULL) //扫描单链表L
{
free(pre); //释放*pre节点
pre=p; //pre和p同步后移一个节点
p=pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向为节点,需要单独释放它
}
bool ListEmpty(LinkList *L) //判断是否为空表
{
return(L->next == NULL);
}
int ListLength(LinkList *L) //求线性表长度,返回单链表L中的数据节点的个数
{
int n=0;
LinkList *p=L; //p指向头结点,n置为0,即头结点的序号为0
while(p->next != NULL)
{
n++;
p = p->next;
}
return(n); //循环结束,p指向为节点,其序号n即为节点个数
}
bool GetElem(LinkList *L, int i, ElemType *e) //返回线性表中第i个元素的值给e
{
int j=0;
LinkList *p=L; //p指向头结点,j置为0,即头结点的序号为0
while(j<i && p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
{
printf("The %d data no exist!\n", i); //第i个数据节点不存在,返回false
return false;
}
else
{
*e=p->data; //存在第i个数据节点,返回true
return true;
}
}
int LocateElem(LinkList *L, ElemType e) //查找第1个值域与e相等的元素的逻辑位序,不存在则返回值为0
{
int i=1;
LinkList *p=L->next; //p指向开始节点,i置为1
while(p!=NULL && p->data!=e) //查找data值为e的节点,其序号为i
{
p=p->next;
i++;
}
i++;
if(p==NULL)
return 0;
else
return i;
} //算法时间复杂度为O(n),不具有随机存取特性
bool ListInsert(LinkList **L, int i, ElemType e) //在单链表逻辑序号的第i个位置插入新的元素e
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点,将值为e的节点插入其后
LinkList *p=(*L), *s; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点*p,插入新节点并返回true
{
s=(LinkList*)malloc(sizeof(LinkList)); //创建新节点*s,其data域置为e
s->data=e;
s->next=p->next; //s插入到p之后
p->next=s;
return true;
}
}
bool ListDelete(LinkList **L, int i, ElemType *e) //删除单链表逻辑序号的第i个元素
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点并且后继节点也存在,则删除该后继节点
LinkList *p=(*L), *q; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点
{
q=p->next; //q指向第i个节点
if(q==NULL) //若不存在第i个节点,返回false
return false;
*e=q->data;
p->next=q->next; //从单链表中删除*q节点并释放
free(q);
return true;
}
}
void delmaxnode(LinkList **L) //删除单链表L中元素值最大的节点(假定最大值节点唯一)
{
LinkList *p, *pre, *maxp, *maxpre; //pre p是一对同步指针
p=(*L)->next;
pre=*L;
maxp=p;
maxpre=pre; //maxpre和maxp用于同步记录pre p
while(p!=NULL)
{
if(maxp->data < p->data) //若找到更大节点则更改maxp和maxpre
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p->next; //p和pre同步后移
}
maxpre->next = maxp->next; //删除*maxp节点
free(maxp);
}
void sort(LinkList **L) //带头结点的单链表L(至少一个数据节点),本算法使其递增有序排列
{
LinkList *p, *pre, *q; //思路:将L拆分为2部分,第一部分是头结点加第一个数据节点形成有序单链表
p = (*L)->next->next; //第二部分是从原第2个数据节点开始的剩余全部数据节点
(*L)->next->next = NULL;
while(p != NULL) //q用于保存*p的后继结点的指针
{
q = p->next;
pre = *L;
while(pre->next != NULL && //从有序表头进行比较,查找pre,在其之后插入*p节点
pre->next->data < p->data)
pre = pre->next;
p->next = pre->next; //继续扫描第二部分余下的节点
pre->next = p;
p = q;
}
} //该算法时间复杂度为O(n^2)
void Reverse(LinkList **L) //将一个带头结点的单链表所有节点逆置
{
LinkList *p = (*L)->next, *q;
(*L)->next = NULL;
while(p != NULL) //头插法
{
q = p->next;
p->next = (*L)->next;
(*L)->next = p;
p = q;
}
}
void split(LinkList **L, LinkList **L1, LinkList **L2)
{
LinkList *p = (*L)->next, *q, *r1;
*L1 = *L;
r1 = *L1;
*L2 = (LinkList*)malloc(sizeof(LinkList));
(*L2)->next = NULL;
while(p != NULL)
{
r1->next = p;
r1 = p;
p = p->next;
q = p->next;
p->next = (*L2)->next;
(*L2)->next = p;
p = q;
}
r1->next = NULL;
}
#include "Headfile.h"
typedef char ElemType;
#include "LinkList Lnode.h"
#include "LinkList basic operation.c"
int main()
{
char a[]={"aAbBcCdDeEfF"};
LinkList *L, *L1, *L2;
L = (LinkList*)malloc(sizeof(LinkList))
CreateListF(&L, a, 12);
DispList(L);
printf("The reverse LinkList:\n");
Reverse(&L);
DispList(L);
split(&L, &L1, &L2);
printf("The L1 linklist:\n");
DispList(L1);
printf("The L2 linklist:\n");
DispList(L2);
int n = ListLength(L2);
printf("The Length of L2 is %d", n);
DestroyList(&L2);
DestroyList(&L1);
return 0;
}
综合以上,个人认为李版定义配合引用型参数的使用,编写的程序逻辑上更清楚易懂,避免了双重指针的使用(往往把人绕晕),可读性更好。
先看严版和大话办的单链表节点定义:
typedef struct LNode
{
ElemType data;
struct LNode *next;
} LNode, *LinkList;
注意这里LinkList直接定义为节点指针了,下文的程序中可以看到使用这个定义去定义某个新节点指针时前面将不带*, 所以不够直观,还要打开节点定义文才能准确无误明白其是指针,尤其是在节点定义与主函数不在同一个文件时。
再看李版单链表节点定义:typedef struct LNode
{
ElemType data;
struct LNode *next;
} LinkList;
并没有直接将LinkList定义为节点指针,这样在程序中定义新节点指针时前面将带有*,比较直观。
下面是根据严版节点定义用C语言实现的单链表操作:
void CreateListF(LinkList *L, ElemType a[], int n) //头插法整体创建单链表,注意这里*L实质上是双重指针,即**LinkList. 下同。
{
LinkList s; //根据严版定义虽然这里没有*,但s 实际是指针,不妨称为隐式指针
int i;
(*L) = (LinkList)malloc(sizeof(LNode)); //创建头结点
if(!L)
exit(OVERFLOW);
(*L)->next = NULL; //头结点next域置为NULL
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList)malloc(sizeof(LNode));
s->data = a[i]; //将s插在头结点之后原开始节点之前
s->next = (*L)->next;
(*L)->next = s;
}
}
void CreateListR(LinkList *L, ElemType a[], int n) //尾插法整体创建单链表
{
LinkList s, r;
int i;
(*L) = (LinkList)malloc(sizeof(LNode)); //创建头结点
if(!(*L))
exit(OVERFLOW);
r= (*L); //r始终指向尾节点,开始时指向头结点
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList)malloc(sizeof(LNode));
s->data = a[i]; //将s插在r之后
r->next = s;
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void InitList(LinkList *L) //初始化线性表(单链表),即创建一个头结点
{
(*L) = (LinkList)malloc(sizeof(LNode));
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
(*L)->next = NULL;
}
void DestroyList(LinkList *L) //销毁线性表
{
LinkList pre=(*L), p=(*L)->next; //pre指向*p的前驱节点
while(p != NULL) //扫描单链表L
{
free(pre); //释放*pre节点
pre=p; //pre和p同步后移一个节点
p=pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向为节点,需要单独释放它
printf("Destroy success!\n");
}
bool ListEmpty(LinkList L) //判断是否为空表
{
return(L->next == NULL);
}
int ListLength(LinkList L) //求线性表长度,返回单链表L中的数据节点的个数
{
int n=0;
LinkList p=L; //p指向头结点,n置为0,即头结点的序号为0
while(p->next != NULL)
{
n++;
p = p->next;
}
return(n); //循环结束,p指向为节点,其序号n即为节点个数
}
void DispList(LinkList L) //输出线性表
{
LinkList p = L->next; //p指向开始节点
while(p != NULL) //p不为NULL,输出*p节点的data域
{
printf("%c ", p->data);
p = p->next; //移向下一个节点
}
printf("\n");
}
bool GetElem(LinkList L, int i, ElemType *e) //返回线性表中第i个元素的值给e
{
int j=0;
LinkList p=L; //p指向头结点,j置为0,即头结点的序号为0
while(j<i && p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
{
printf("The %d data no exist!\n", i); //第i个数据节点不存在,返回false
return false;
}
else
{
*e=p->data; //存在第i个数据节点,返回true
return true;
}
}
int LocateElem(LinkList L, ElemType e) //查找第1个值域与e相等的元素的逻辑位序,不存在则返回值为0
{
int i=1;
LinkList p=L->next; //p指向开始节点,i置为1
while(p!=NULL && p->data!=e) //查找data值为e的节点,其序号为i
{
p=p->next;
i++;
}
i++;
if(p==NULL)
return 0;
else
return i;
} //算法时间复杂度为O(n),不具有随机存取特性
bool ListInsert(LinkList *L, int i, ElemType e) //在单链表逻辑序号的第i个位置插入新的元素e
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点,将值为e的节点插入其后
LinkList p=(*L), s; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点*p,插入新节点并返回true
{
s=(LinkList)malloc(sizeof(LNode)); //创建新节点*s,其data域置为e
s->data=e;
s->next=p->next; //s插入到p之后
p->next=s;
return true;
}
}
bool ListDelete(LinkList *L, int i, ElemType *e) //删除单链表逻辑序号的第i个元素
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点并且后继节点也存在,则删除该后继节点
LinkList p=(*L), q; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点
{
q=p->next; //q指向第i个节点
if(q==NULL) //若不存在第i个节点,返回false
return false;
*e=q->data;
p->next=q->next; //从单链表中删除*q节点并释放
free(q);
return true;
}
}
void delmaxnode(LinkList *L) //删除单链表L中元素值最大的节点(假定最大值节点唯一)
{
LinkList p, pre, maxp, maxpre; //pre p是一对同步指针
p=(*L)->next;
pre=(*L);
maxp=p;
maxpre=pre; //maxpre和maxp用于同步记录pre p
while(p!=NULL)
{
if(maxp->data < p->data) //若找到更大节点则更改maxp和maxpre
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p->next; //p和pre同步后移
}
maxpre->next = maxp->next; //删除*maxp节点
free(maxp);
}
void sort(LinkList *L) //带头结点的单链表L(至少一个数据节点),本算法使其递增有序排列
{
LinkList p, pre, q; //思路:将L拆分为2部分,第一部分是头结点加第一个数据节点形成有序单链表
p = (*L)->next->next; //第二部分是从原第2个数据节点开始的剩余全部数据节点
(*L)->next->next = NULL;
while(p != NULL) //q用于保存*p的后继结点的指针
{
q = p->next;
pre = (*L);
while(pre->next != NULL && //从有序表头进行比较,查找pre,在其之后插入*p节点
pre->next->data < p->data)
pre = pre->next;
p->next = pre->next; //继续扫描第二部分余下的节点
pre->next = p;
p = q;
}
} //该算法时间复杂度为O(n^2)
void Reverse(LinkList *L) //将一个带头结点的单链表所有节点逆置
{
LinkList p = (*L)->next, q;
(*L)->next = NULL;
while(p != NULL) //头插法
{
q = p->next;
p->next = (*L)->next;
(*L)->next = p;
p = q;
}
}
void split(LinkList *L, LinkList *L1, LinkList *L2)
{
LinkList p = (*L)->next, q, r1;
(*L1) = (*L);
r1 = *L1;
(*L2) = (LinkList)malloc(sizeof(LNode));
(*L2)->next = NULL;
while(p != NULL)
{
r1->next = p;
r1 = p;
p = p->next;
q = p->next;
p->next = (*L2)->next;
(*L2)->next = p;
p = q;
}
r1->next = NULL;
}
/*这是基于严版定义只用C语言实现的单链表算法的主程序(使用了部分基本操作函数) */
#include <stdio.h>
#include<stdlib.h>
#include <stdbool.h>
#include <math.h>
typedef char ElemType;
#define OVERFLOW -1
#include "LinkList Lnode.h"
#include "LinkList basic operation.cpp"
int main()
{
int n;
char a[]={"aAbBcCdDeEfF"};
LinkList L, L1, L2; //根据严版定义虽然这里没有*,但L, L1, L2 实际是指针
CreateListF(&L, a, 12); //这里&是取地址
printf("The original LinkList:\n");
DispList(L);
printf("The reverse LinkList:\n");
Reverse(&L);
DispList(L);
split(&L, &L1, &L2);
printf("The L1 linklist:\n");
DispList(L1);
printf("The L2 linklist:\n");
DispList(L2);
n = ListLength(L2);
printf("n=%d\n", n);
DestroyList(&L2);
return 0;
}
李春葆老师在MOOC视频里特地强调了输出型参数引用的使用,将引用符号"&"放在形参的前面。
由于C语言编译器不支持引用参数 ,所以要用C++编译器编译程序,尽管程序基本全是C语言写成的。
通过下面的程序也可以看出基于李版节点定义编程时,该定义非常适合引用参数的使用,比C语言使用
指针更方便。下面是相对应的具体操作函数实现和主程序(C++编译器):
void CreateListF(LinkList *&A, ElemType a[], int n) //头插法整体创建单链表,注意*&A,表示引用参数L. 下同.
{
LinkList *s;
int i;
A = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!A)
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
A->next = NULL; //头结点next域置为NULL
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在头结点之后原开始节点之前
s->next = A->next;
A->next = s;
}
}
void CreateListR(LinkList *&L, ElemType a[], int n) //尾插法整体创建单链表,链表的节点顺序与逻辑次序相同
{
LinkList *s, *r;
int i;
L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!L)
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
r = L; //r始终指向尾节点,开始时指向头结点
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在r之后
r->next = s;
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void InitList(LinkList *&L) //初始化线性表(单链表),即创建一个头结点
{
L = (LinkList*)malloc(sizeof(LinkList));
if(!L)
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
L->next = NULL;
}
void DestroyList(LinkList *&L) //销毁线性表
{
LinkList *pre=L, *p=L->next; //pre指向*p的前驱节点
while(p != NULL) //扫描单链表L
{
free(pre); //释放*pre节点
pre=p; //pre和p同步后移一个节点
p=pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向为节点,需要单独释放它
}
bool ListEmpty(LinkList *L) //判断是否为空表
{
return(L->next == NULL);
}
int ListLength(LinkList *L) //求线性表长度,返回单链表L中的数据节点的个数
{
int n=0;
LinkList *p=L; //p指向头结点,n置为0,即头结点的序号为0
while(p->next != NULL)
{
n++;
p = p->next;
}
return(n); //循环结束,p指向为节点,其序号n即为节点个数
}
void DispList(LinkList *L) //输出线性表
{
LinkList *p = L->next; //p指向开始节点
while(p != NULL) //p不为NULL,输出*p节点的data域
{
printf("%c ", p->data);
p = p->next; //移向下一个节点
}
printf("\n");
}
bool GetElem(LinkList *L, int i, ElemType &e) //返回线性表中第i个元素的值给e
{
int j=0;
LinkList *p=L; //p指向头结点,j置为0,即头结点的序号为0
while(j<i && p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
{
printf("The %d data no exist!\n", i); //第i个数据节点不存在,返回false
return false;
}
else
{
e=p->data; //存在第i个数据节点,返回true
return true;
}
}
int LocateElem(LinkList *L, ElemType e) //查找第1个值域与e相等的元素的逻辑位序,不存在则返回值为0
{
int i=1;
LinkList *p=L->next; //p指向开始节点,i置为1
while(p!=NULL && p->data!=e) //查找data值为e的节点,其序号为i
{
p=p->next;
i++;
}
i++;
if(p==NULL)
return 0;
else
return i;
} //算法时间复杂度为O(n),不具有随机存取特性
bool ListInsert(LinkList *&L, int i, ElemType e) //在单链表逻辑序号的第i个位置插入新的元素e
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点,将值为e的节点插入其后
LinkList *p=L, *s; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点*p,插入新节点并返回true
{
s=(LinkList*)malloc(sizeof(LinkList)); //创建新节点*s,其data域置为e
s->data=e;
s->next=p->next; //s插入到p之后
p->next=s;
return true;
}
}
bool ListDelete(LinkList *&L, int i, ElemType &e) //删除单链表逻辑序号的第i个元素
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点并且后继节点也存在,则删除该后继节点
LinkList *p=L, *q; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点
{
q=p->next; //q指向第i个节点
if(q==NULL) //若不存在第i个节点,返回false
return false;
e=q->data;
p->next=q->next; //从单链表中删除*q节点并释放
free(q);
return true;
}
}
void delmaxnode(LinkList *&L) //删除单链表L中元素值最大的节点(假定最大值节点唯一)
{
LinkList *p, *pre, *maxp, *maxpre; //pre p是一对同步指针
p=L->next;
pre=L;
maxp=p;
maxpre=pre; //maxpre和maxp用于同步记录pre p
while(p!=NULL)
{
if(maxp->data < p->data) //若找到更大节点则更改maxp和maxpre
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p->next; //p和pre同步后移
}
maxpre->next = maxp->next; //删除*maxp节点
free(maxp);
}
void sort(LinkList *&L) //带头结点的单链表L(至少一个数据节点),本算法使其递增有序排列
{
LinkList *p, *pre, *q; //思路:将L拆分为2部分,第一部分是头结点加第一个数据节点形成有序单链表
p = L->next->next; //第二部分是从原第2个数据节点开始的剩余全部数据节点
L->next->next = NULL;
while(p != NULL) //q用于保存*p的后继结点的指针
{
q = p->next;
pre = L;
while(pre->next != NULL && //从有序表头进行比较,查找pre,在其之后插入*p节点
pre->next->data < p->data)
pre = pre->next;
p->next = pre->next; //继续扫描第二部分余下的节点
pre->next = p;
p = q;
}
} //该算法时间复杂度为O(n^2)
void Reverse(LinkList *&L) //将一个带头结点的单链表所有节点逆置
{
LinkList *p = L->next, *q;
L->next = NULL;
while(p != NULL) //头插法
{
q = p->next;
p->next = L->next;
L->next = p;
p = q;
}
}
void split(LinkList *&L, LinkList *&L1, LinkList *&L2)
{
LinkList *p = L->next, *q, *r1;
L1 = L;
r1 = L1;
L2 = (LinkList*)malloc(sizeof(LinkList));
L2->next = NULL;
while(p != NULL)
{
r1->next = p;
r1 = p;
p = p->next;
q = p->next;
p->next = L2->next;
L2->next = p;
p = q;
}
r1->next = NULL;
}
#include "Headfile.h"
typedef char ElemType;
#include "LinkList Lnode.h"
#include "LinkList basic operation.cpp"
//typedef char ElemType;
int main()
{
char a[]={"aAbBcCdDeEfF"};
LinkList *L, *L1, *L2; //这里有*,一目了然知道是指针
CreateListF(L, a, 12); //函数调用时无需取地址符&了
printf("The original LinkList:\n");
DispList(L);
printf("The reverse LinkList:\n");
Reverse(L);
DispList(L);
split(L, L1, L2);
printf("The L1 linklist:\n");
DispList(L1);
printf("The L2 linklist:\n");
DispList(L2);
DestroyList(L2);
int n = ListLength(L2);
return 0;
}
如果强调一定要用C编译器的情况,基于李版定义也能实现,显式的使用了双重指针;
而严版定义程序其实是隐式的使用了双重指针。下面是C编译器下李版定义程序:
/*C编译器下基于李版节点定义的操作函数和主程序 */
void CreateListF(LinkList **L, ElemType a[], int n) //头插法整体创建单链表,注意这里L前面是2个*,即指针的指针
{ //这样就可以改变主函数传递过来的内容并返回!
LinkList *s; //显示指针
int i;
*L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
(*L)->next = NULL; //头结点next域置为NULL
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在头结点之后原开始节点之前
s->next = (*L)->next;
(*L)->next = s;
}
printf("子函数*L创建后的的地址 %p\n",*L);
printf("子函数L创建后的地址 %p\n",L);
}
void DispList(LinkList *L) //输出线性表
{
LinkList *p = L->next; //p指向开始节点
while(p != NULL) //p不为NULL,输出*p节点的data域
{
printf("%c ", p->data);
p = p->next; //移向下一个节点
}
printf("到这里是正确的!\n");
}
void CreateListR(LinkList **L, ElemType a[], int n) //尾插法整体创建单链表,链表的节点顺序与逻辑次序相同
{
LinkList *s, *r;
int i;
*L = (LinkList*)malloc(sizeof(LinkList)); //创建头结点
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
r = (*L); //r始终指向尾节点,开始时指向头结点
for(i=0; i<n; i++) //循环建立数据节点
{
s = (LinkList*)malloc(sizeof(LinkList));
s->data = a[i]; //将s插在r之后
r->next = s;
r = s;
}
r->next = NULL; //尾结点next域置为NULL
}
void InitList(LinkList **L) //初始化线性表(单链表),即创建一个头结点
{
*L = (LinkList*)malloc(sizeof(LinkList));
if(!(*L))
{
printf("Memory no sufficient space!\n");
exit(OVERFLOW);
}
(*L)->next = NULL;
}
void DestroyList(LinkList **L) //销毁线性表
{
LinkList *pre=*L, *p=(*L)->next; //pre指向*p的前驱节点
while(p != NULL) //扫描单链表L
{
free(pre); //释放*pre节点
pre=p; //pre和p同步后移一个节点
p=pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向为节点,需要单独释放它
}
bool ListEmpty(LinkList *L) //判断是否为空表
{
return(L->next == NULL);
}
int ListLength(LinkList *L) //求线性表长度,返回单链表L中的数据节点的个数
{
int n=0;
LinkList *p=L; //p指向头结点,n置为0,即头结点的序号为0
while(p->next != NULL)
{
n++;
p = p->next;
}
return(n); //循环结束,p指向为节点,其序号n即为节点个数
}
bool GetElem(LinkList *L, int i, ElemType *e) //返回线性表中第i个元素的值给e
{
int j=0;
LinkList *p=L; //p指向头结点,j置为0,即头结点的序号为0
while(j<i && p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
{
printf("The %d data no exist!\n", i); //第i个数据节点不存在,返回false
return false;
}
else
{
*e=p->data; //存在第i个数据节点,返回true
return true;
}
}
int LocateElem(LinkList *L, ElemType e) //查找第1个值域与e相等的元素的逻辑位序,不存在则返回值为0
{
int i=1;
LinkList *p=L->next; //p指向开始节点,i置为1
while(p!=NULL && p->data!=e) //查找data值为e的节点,其序号为i
{
p=p->next;
i++;
}
i++;
if(p==NULL)
return 0;
else
return i;
} //算法时间复杂度为O(n),不具有随机存取特性
bool ListInsert(LinkList **L, int i, ElemType e) //在单链表逻辑序号的第i个位置插入新的元素e
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点,将值为e的节点插入其后
LinkList *p=(*L), *s; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点*p,插入新节点并返回true
{
s=(LinkList*)malloc(sizeof(LinkList)); //创建新节点*s,其data域置为e
s->data=e;
s->next=p->next; //s插入到p之后
p->next=s;
return true;
}
}
bool ListDelete(LinkList **L, int i, ElemType *e) //删除单链表逻辑序号的第i个元素
{
int j=0; //思路:先在单链表L中找到第i-1个节点*p,若存在这样的节点并且后继节点也存在,则删除该后继节点
LinkList *p=(*L), *q; //p指向头结点,j置为0
while(j<i-1 && p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else //找到第i-1个节点
{
q=p->next; //q指向第i个节点
if(q==NULL) //若不存在第i个节点,返回false
return false;
*e=q->data;
p->next=q->next; //从单链表中删除*q节点并释放
free(q);
return true;
}
}
void delmaxnode(LinkList **L) //删除单链表L中元素值最大的节点(假定最大值节点唯一)
{
LinkList *p, *pre, *maxp, *maxpre; //pre p是一对同步指针
p=(*L)->next;
pre=*L;
maxp=p;
maxpre=pre; //maxpre和maxp用于同步记录pre p
while(p!=NULL)
{
if(maxp->data < p->data) //若找到更大节点则更改maxp和maxpre
{
maxp = p;
maxpre = pre;
}
pre = p;
p = p->next; //p和pre同步后移
}
maxpre->next = maxp->next; //删除*maxp节点
free(maxp);
}
void sort(LinkList **L) //带头结点的单链表L(至少一个数据节点),本算法使其递增有序排列
{
LinkList *p, *pre, *q; //思路:将L拆分为2部分,第一部分是头结点加第一个数据节点形成有序单链表
p = (*L)->next->next; //第二部分是从原第2个数据节点开始的剩余全部数据节点
(*L)->next->next = NULL;
while(p != NULL) //q用于保存*p的后继结点的指针
{
q = p->next;
pre = *L;
while(pre->next != NULL && //从有序表头进行比较,查找pre,在其之后插入*p节点
pre->next->data < p->data)
pre = pre->next;
p->next = pre->next; //继续扫描第二部分余下的节点
pre->next = p;
p = q;
}
} //该算法时间复杂度为O(n^2)
void Reverse(LinkList **L) //将一个带头结点的单链表所有节点逆置
{
LinkList *p = (*L)->next, *q;
(*L)->next = NULL;
while(p != NULL) //头插法
{
q = p->next;
p->next = (*L)->next;
(*L)->next = p;
p = q;
}
}
void split(LinkList **L, LinkList **L1, LinkList **L2)
{
LinkList *p = (*L)->next, *q, *r1;
*L1 = *L;
r1 = *L1;
*L2 = (LinkList*)malloc(sizeof(LinkList));
(*L2)->next = NULL;
while(p != NULL)
{
r1->next = p;
r1 = p;
p = p->next;
q = p->next;
p->next = (*L2)->next;
(*L2)->next = p;
p = q;
}
r1->next = NULL;
}
#include "Headfile.h"
typedef char ElemType;
#include "LinkList Lnode.h"
#include "LinkList basic operation.c"
int main()
{
char a[]={"aAbBcCdDeEfF"};
LinkList *L, *L1, *L2;
L = (LinkList*)malloc(sizeof(LinkList))
CreateListF(&L, a, 12);
DispList(L);
printf("The reverse LinkList:\n");
Reverse(&L);
DispList(L);
split(&L, &L1, &L2);
printf("The L1 linklist:\n");
DispList(L1);
printf("The L2 linklist:\n");
DispList(L2);
int n = ListLength(L2);
printf("The Length of L2 is %d", n);
DestroyList(&L2);
DestroyList(&L1);
return 0;
}
综合以上,个人认为李版定义配合引用型参数的使用,编写的程序逻辑上更清楚易懂,避免了双重指针的使用(往往把人绕晕),可读性更好。
阅读全文
0 0
- 关于数据结构课程里不同节点定义对程序实现的影响
- 关于多线程对程序执行时间的影响
- c与c++程序的不同对调试的影响
- 探寻不同版本的SDK对iOS程序的影响
- 关于arm ads 上RO_BASE对程序速度的影响
- 浏览器对程序的影响
- JDk版本不同对系统的影响
- 不同公司对BI的不同定义
- C语言中的字节对齐和对程序的影响以及位段的定义
- lua5.1 和 5.2 关于 sequence 的定义变化,对#table取值的影响
- 关于定义class时前面加不加所在的标签,及其对优先级的影响
- 虚函数对数据结构内存的影响
- Hadoop对各个节点的角色定义
- Hadoop对各个节点的角色定义
- C++模板实现二叉查找树(一 树的数据结构定义与节点插入)
- EBS系统<关于Oracle数据库的包的编译(对AP程序的)影响>
- [J2ME]关于移动的主页推送对J2ME程序的影响
- 关于传指针还是引用的对程序速度的影响
- Windows下Maven私服怎么搭建
- window启动过程
- 20. Valid Parentheses
- iOS开发学习笔记——UIWindow
- SELINUX
- 关于数据结构课程里不同节点定义对程序实现的影响
- Docker和宿主机之间共享文件
- 业务架构改进一
- [BZOJ4008][HNOI2015]亚瑟王 期望DP
- JavaScript对象继承
- 2222
- Hibernate基本知识概括
- 创建并运行一个基本的Python测试程序(自己的看法修改)
- POJ1904:King's Quest(强连通 & 二分图)