因为教材是用的C++,所以今天的代码是用C++实现的
//单链表的定义
typedef struct LNode
{ElemType data;struct LNode *next;
}LNode,*LinkList;//初始化
Status InitList(LinkList &L)
{L=new LNode;L->next=NULL;return OK;
}//取值
Status GetElem(LinkList L ,int i,ElemType &e){p=L->next;;j=1;while(p&&j<i){p=p->next;++j;}if(!p||j>i)return ERRPR;e=p->data;return OK;}//查找
LNode *LocateElem(LinkList L,ElemType e){p=L->next;while(p && p->data!=e)p=p->next;return p;
}//插入
Status ListInsert(LinkList &L,int i,ElemType e){p=L;j=0;while(p && (j<i-1)){p=p->next;++j;}if(!p||j>i-1) return ERROR;s=new LNode;s->data=e;s->next=p->next;p->next=s;return OK;
}//删除
Status ListDelete(LinkList &L,int i){p=L;j=0;while((p->next) && (j<i-1)){j++;p=p->next;}if(!(p->next)||j>i-1) return ERROR;q=p->next;p->next=q->next;delete q;return OK;
}//前插法创建单链表
void CreateList_H(LinkList &L,int n){L=new LNode;L->next=NULL;for(i=0;i<n;++i){p=new LNode;cin>>p->data;p->next=L->next;L->next=p;}
}//后插法创建单链表
void CreateList_R(LinkList &L,int n){L=new Lnode;L->next=NULL;r=L;for(i=0;i<n;i++){p=new LNode;cin>>p->data;p->next=NULL;r->next=p;r=p;}
}//循环链表的定义及初始化
typedef struct LNode
{ElemType data;struct LNode *next;
}LNode,*LinkList;Status InitList(LinkList &L)
{L=new LNode;L->next=L; //只有这里跟单链表不同,其余地方都一样return OK;
}//双向链表的定义及初始化
typedef struct DuLNode{ElemType data;struct DuLNode *prior;struct DuLNode *next;
}DuLNode,*DuLinkList;Status InitList(DuLinkList &L){L=new DuLNode;L->next=L;L->prior=L;return OK;
}//双向链表的插入
Status ListInsert_DuL(DuLinkList &L,int i,ElemType e){if(!(p=GetElem_Dul(L,i)))return ERROR;s=new DuLinkList;s->data=e;a=p->prior;p->prior=s;s->prior=a;a->next=s;s->next=p;return OK;
}//双向链表的删除
Status ListDelete_DuL(DuLinkList &L,int i){if(!(p=GetElem_Dul(L,i)))return ERROR;p->next->prior=p->prior;p->prior->next=p->next;delete p;return OK;
}
链表的工作原理
在一个链表中,每个节点都有一个 next
指针,指向下一个节点。例如,假设有两个节点:
LNode *node1 = new LNode;
LNode *node2 = new LNode;node1->next = node2; // node1 的 next 指向 node2
在这个例子中,node1
的 next
指向 node2
,表示链表中的第一个节点指向第二个节点。通过这种方式,节点可以连接起来,形成一个链表。
动态创建新的节点
这行代码 LNode *node1 = new LNode;
的意思是动态创建一个新的 LNode
类型的节点,并将其地址赋值给指针 node1
。
new LNode
:
new
是一个运算符,用于在堆上动态分配内存。LNode
表示你要创建一个新的LNode
结构体实例。这段代码会在堆上分配足够的内存来存储一个LNode
的数据,并返回这个新创建对象的地址。
初始化
-
函数定义:
-
Status InitList(LinkList &L)
: 这是函数的声明,函数名为InitList
,返回类型为Status
(表示操作的状态,如成功或失败),接受一个引用类型的参数LinkList &L
。这里的LinkList
是指向LNode
的指针类型。
-
-
创建新的节点:
-
L=new LNode;
: 这一行代码使用new
关键字在堆上动态分配一个新的LNode
结构的内存,并将这个新节点的地址赋值给L
。这意味着L
现在指向一个新的链表节点。
-
-
初始化
next
指针:-
L->next=NULL;
: 将新节点的next
指针设置为NULL
,表示当前链表中只有一个节点(头节点),而且没有后继节点。链表的末尾通常用NULL
来表示。
-
-
返回状态:
-
return OK;
: 函数结束时返回一个状态值OK
,表示链表成功初始化。
-
引用类型
LinkList
:这是一个指向LNode
类型的指针(即LNode*
的别名)。&L
:表示L
是LinkList
类型的引用。这意味着L
引用的是一个指向LNode
的指针。
在 C++ 中,引用是一种特殊的变量,它允许你直接访问另一个变量的内存,而不是创建该变量的副本。当你使用引用作为参数时,函数可以直接操作传入的变量,而不需要返回值来修改它。(可以对比上个笔记C语言的访问创建副本)
举个例子
假设你有一个函数:
void modify(int &x) {x = 10; // 修改了引用变量 x
}
在 void modify(int &x)
中,x
是一个 int
类型的引用。这意味着:
-
引用的性质:
x
并不是一个新的变量,而是直接引用了传入的整型变量。换句话说,x
是传入整型变量的别名。 -
改变引用的值:当你在
modify
函数中对x
进行修改时,实际上是在修改传入的那个整型变量。因此,任何对x
的改变都会直接影响到原始的变量。
如果你调用这个函数:
int a = 5;
modify(a); // a 现在变为 10
C++的引用相较于C语言指针的一些优势:
1. 语法简洁
引用的语法更简洁,因为你不需要使用解引用操作符(*
)来访问引用的变量。
2. 避免空指针
引用必须在定义时初始化,并且不能为 NULL
(或 nullptr
)。这意味着引用始终指向一个有效的对象,而指针可能指向 NULL
,从而引发潜在的空指针异常。
3. 更自然的语义
引用更像是对变量的别名,能够更清晰地表达意图。在许多情况下,使用引用的代码更易读,更容易理解。
4. 简化参数传递
在函数参数中使用引用,可以直接修改传入的变量,而不需要返回值。
取值
关于 L
的含义
在代码中:
L
是整个链表的“入口”,也就是头指针。- 头指针
L
通常指向一个特殊的 头节点(dummy node)。头节点本身不存储实际的数据,只是为了方便操作整个链表,比如插入、删除和遍历等。 L->next
指向链表的第一个有效节点(首元节点)。- 如果链表为空,
L->next == NULL
。
e = p->data
的意义
假设 p
当前指向第 i
个节点:
p->data
表示这个节点中存储的 数据部分。- 赋值语句
e = p->data
将该节点的数据部分赋值给变量e
。
为什么
p->data
可以访问数据?这是因为
p
是一个指向节点的指针,p->data
表示指针所指向节点的data
成员。
查找
LNode *LocateElem
LNode *LocateElem(LinkList L, ElemType e)
LNode *
表示LocateElem函数返回的是一个指向链表节点的指针。
前插法创建单链表
cin>>p->data;
>>
是 C++ 中的输入运算符。它是从标准输入(通常是键盘)中读取数据的方式之一。
- 在 C++ 中,
>>
是 流提取运算符(stream extraction operator)。 - 它用于从输入流中提取数据,并将数据存储到指定的变量中。
cin
是 C++ 的标准输入流对象,通常用于从键盘获取输入。