线性表——循环链表
引用或指针作为函数参数——参数地址参与判断条件
如下为循环链表所有代码
#include
#define ERROR 0
#define OK 1
#define NOTFOUND 0
using namespace std;
typedef char ElemType;
/*定义结点和循环链表*/
typedef struct LinkedNode {
ElemType data;
LinkedNode *next;
} CLinkedList;
/*初始化循环链表*/
void InitList(CLinkedList &L){
L.next = &L;
}
/*检查循环链表是否为空*/
bool ListEmpty(CLinkedList L){
return (L.next == &L);
}
/*取得链表中索引对应的地址*/
LinkedNode *getAddress(CLinkedList &L,int i){
LinkedNode *temp = &L;
for(int j=0;jnext;
}
return temp;
}
/*返回链表已有元素数量*/
int ListLength(CLinkedList &L){
LinkedNode *temp = &L;
int c =0;
while(temp->next!=&L){
temp = temp->next;
c++;
}
return c;
}
/*取得第i的元素,并将其代入e中*/
int GetElem(CLinkedList &L,int i,ElemType &e){
if(ListEmpty(L)||!(i>=1&&i<= ListLength(L))) return ERROR;
e = getAddress(L,i)->data;
return OK;
}
/*返回第一个数据域为x的结点所在索引*/
int LocateElem(CLinkedList &L,ElemType x){
if(ListEmpty(L)) return NOTFOUND;
LinkedNode *temp = L.next;
int i=1;
while(temp!=&L&&temp->data!=x){
i++;
temp = temp->next;
}
if(i== ListLength(L)+1) return NOTFOUND;
return i;
}
/*使用头插法插入单个结点,返回插入结点的地址*/
LinkedNode *ListInsertL(CLinkedList &L,ElemType x){
LinkedNode *toAdd = new LinkedNode;
toAdd->data = x;
toAdd->next = L.next;
L.next = toAdd;
return toAdd;
}
/*使用头插法插入由数组组成的多个结点,n代表数组的长度*/
int ListArrInsertL(CLinkedList &L,ElemType arr[],int n){
for(int i=0;idata = x;
LinkedNode *temp = getAddress(L, ListLength(L));
toAdd->next = temp->next;
temp->next = toAdd;
return toAdd;
}
/*使用尾插法插入由数组组成的多个结点,n代表数组的长度*/
int ListArrInsertR(CLinkedList &L,ElemType arr[],int n){
for(int i=0;inext;
p->next = q->next;
/*将被删除结点q的数据代入e中*/
e = q->data;
/*在内存空间中释放结点q*/
free(q);
}
/*展示循环链表所有元素*/
void DispLinkedList(CLinkedList &clinkedList) {
if(ListEmpty(clinkedList)) return;
/*temp指针用于遍历链表中所有的数据元素*/
LinkedNode *temp = clinkedList.next;
cout << &clinkedList << endl;
int i = 1;
while(temp!=&clinkedList){
/*输出移动变量的地址和循环链表头指针的地址*/
cout << "temp:" << temp << " | &clinkedList:" << &clinkedList << endl;
/*输出数据元素以及所在索引*/
cout<<"elment "<data<next;
}
cout<
重点考察如下部分:
/*展示循环链表所有元素*/
void DispLinkedList(CLinkedList clinkedList) {
if(ListEmpty(clinkedList)) return;
/*temp指针用于遍历链表中所有的数据元素*/
LinkedNode *temp = clinkedList.next;
cout << &clinkedList << endl;
int i = 1;
while(temp!=&clinkedList){
/*输出temp的地址和循环链表头指针的地址*/
cout << "temp:" << temp << " | &clinkedList:" << &clinkedList << endl;
/*输出数据元素以及所在索引*/
cout<<"elment "<data<next;
}
cout<
? 按照一般思维,若是对链表只读,一般用值传递参数,而不是引用或者指针传递参数,因为值传递不影响实参,可以有效保护原有数据,相反,引用或者指针可能会对原数据产生改变[如 void merge_sort(int *arr,int l,int )]。我们尝试将DispLinkedList函数中的参数传递改为值传递,再重新运行代码。
/*展示循环链表所有元素*/
void DispLinkedList(CLinkedList clinkedList) {
if(ListEmpty(clinkedList)) return;
/*temp指针用于遍历链表中所有的数据元素*/
LinkedNode *temp = clinkedList.next;
cout << &clinkedList << endl;
int i = 1;
while(temp!=&clinkedList){
/*输出temp的地址和循环链表头指针的地址*/
cout << "temp:" << temp << " | &clinkedList:" << &clinkedList << endl;
/*输出数据元素以及所在索引*/
cout<<"elment "<data<next;
}
cout<
运行结果:
? 0x61fda0
? temp:0x7616c0 | &clinkedList:0x61fda0
? elment 1 : a
? temp:0x7616a0 | &clinkedList:0x61fda0
? elment 2 : b
? temp:0x761680 | &clinkedList:0x61fda0
? elment 3 : c
? temp:0x61fe10 | &clinkedList:0x61fda0
? elment 4 :
? temp:0x7616c0 | &clinkedList:0x61fda0
? elment 5 : a
? temp:0x7616a0 | &clinkedList:0x61fda0
? elment 6 : b
? temp:0x761680 | &clinkedList:0x61fda0
? elment 7 : c
? temp:0x61fe10 | &clinkedList:0x61fda0
? elment 8 :
? .......
? 发现DispLinkedList中的while循环是个死循环,由输出我们发现,这是因为用于遍历的temp指针的指针永远不等于值传递的循环链表参数的地址。
? 按理说,循环链表CLinkedList最后一个指针的指针域(*next)应该指向循环链表本身的头指针,本应该不会出现死循环。但是,问题在于,当DispLinkedList使用值传递时,会在内存空间中新开辟一个内存地址,而新开辟的内存地址不等于原有循环链表头指针的地址,因而temp!=&cLinkedList恒成立,while进入死循环。
? 因而,为了使参数的地址与原有虚拟换链表的头指针地址相同,我们应该使用引用传递或者指针传递来传递函数参数,如下。
/*展示循环链表所有元素*/
void DispLinkedList(CLinkedList &clinkedList) {
if(ListEmpty(clinkedList)) return;
/*temp指针用于遍历链表中所有的数据元素*/
LinkedNode *temp = clinkedList.next;
cout << &clinkedList << endl;
int i = 1;
while(temp!=&clinkedList){
/*输出移动变量的地址和循环链表头指针的地址*/
cout << "temp:" << temp << " | &clinkedList:" << &clinkedList << endl;
/*输出数据元素以及所在索引*/
cout<<"elment "<data<next;
}
cout<
运行结果:
? 0x61fe10
? temp:0xda16c0 | &clinkedList:0x61fe10
? elment 1 : a
? temp:0xda16a0 | &clinkedList:0x61fe10
? elment 2 : b
? temp:0xda1680 | &clinkedList:0x61fe10
? elment 3 : c
? while-loop end
? temp:0x61fe10 &clinkedList :0x61fe10
? 总结一下,当参数地址参与判断时,使用指针传递或者引用传递,因为以上两种传递会保留原实参的地址,保证判断正确。