线性表——循环链表


引用或指针作为函数参数——参数地址参与判断条件

如下为循环链表所有代码

#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

? 总结一下,当参数地址参与判断时,使用指针传递或者引用传递,因为以上两种传递会保留原实参的地址,保证判断正确。