#include <stdio.h>
#include <string.h>
#include <stdlib.h>typedef int ElemType;     // 自定义链表的数据元素为整数。typedef struct LNode
{ElemType data;       // 存放结点的数据元素。struct LNode *next;  // 指向下一个结点的指针。
}LNode,*LinkList;// 初始化链表LL,返回值:失败返回NULL,成功返回头结点的地址。
LNode *InitList1();// 传入指针变量的地址的方法。
// 初始化链表,返回值:0-失败;1-成功。
int InitList2(LinkList *LL);// C++引用的方法,在Linux下,需要用g++编译。
// 初始化链表,返回值:0-失败;1-成功。
// int InitList3(LinkList &LL);// 如果参数采用转指针LL的值,LL的值只能传进去,无法返回,这种方法是不行的。
int InitList4(LinkList LL);// 销毁链表LL。
void DestroyList1(LinkList LL);// 销毁链表LL。
// 传入指针的地址的方法。
void DestroyList2(LinkList *LL);// C++引用的方法,在Linux下,需要用g++编译。
// 传入指针的地址的方法。
// void DestroyList3(LinkList &LL);// 清空链表。
void ClearList(LinkList LL);                    // 在链表LL的第ii个位置插入元素ee,返回值:0-失败;1-成功。
int  InsertList(LinkList LL, unsigned int ii, ElemType *ee);   // 打印链表中全部的元素。
void PrintList(LinkList LL);                    // 在链表LL的头部插入元素ee,返回值:0-失败;1-成功。
int  PushFront(LinkList LL, ElemType *ee);// 在链表LL的尾部插入元素ee,返回值:0-失败;1-成功。
int  PushBack(LinkList LL, ElemType *ee);// 删除链表LL中的第ii个结点,返回值:0-位置ii不合法;1-成功。
int  DeleteNode(LinkList LL, unsigned int ii);  // 删除链表LL中第一个结点,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL);// 删除链表LL中最后一个结点,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL);// 求链表的长度,返回值:>=0-表LL结点的个数。
int  LengthList(LinkList LL);                   // 判断链表是否为空,返回值:0-非空或失败,1-空。
int IsEmpty(LinkList LL);// 获取链表中第ii个结点,成功返回结点的地址,失败返回空。
// 注意,ii可以取值为0,表示头结点。
LNode *LocateNode(LinkList LL, unsigned int ii);// 查找元素ee在链表LL中的结点地址,如果没找到返回NULL,否则返回结点的地址。
LNode *LocateElem(LinkList LL, ElemType *ee);// 在指定结点pp之后插入元素ee,返回值:0-失败;1-成功。
int InsertNextNode(LNode *pp, ElemType *ee);// 在指定结点pp之前插入元素ee,返回值:0-失败;1-成功。
int InsertPriorNode(LNode *pp, ElemType *ee);// 删除指定结点。
int DeleteNode1(LNode *pp);// 采用归并的方法,将两个升序的链表La和Lb,合并成一个升序的链表Lc。
int MergeList(LinkList La,LinkList Lb,LinkList Lc);// 把链表pp结点之后的结点原地逆置(反转),返回值:0-失败;1-成功。
void ReverseList(LNode *pp);int main()
{LinkList LL=NULL; // 声明链表指针变量。LL=InitList1();     // 初始化链表。// 如果要在函数中对变量进行赋值,必须把变量的地址传入函数。// 指针变量简称指针,如果要在函数中对指针变量赋值,也必须把指针的地址传入函数。// LL是指针,在InitList2函数中,需要把头结点的地址赋值给LL,所以要传入LL的地址。// 指针是变量,用于存放变量的地址,指针不是地址,指针里存放的内容才是地址。// 所以,这里要把指针变量LL的地址传给InitList2()函数。// 各位菜鸡,明白鸟吗?// InitList2(&LL);     // 初始化链表,传入指针变量LL的地址。// InitList3(LL);      // 初始化链表,C++的引用。// 如果参数采用转指针LL的值,LL的值只能传进去,无法返回,这种方法是不行的。// InitList4(LL);printf("LL=%p\n",LL);ElemType ee;      // 创建一个数据元素。printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");ee=1;  InsertList(LL, 1, &ee);ee=2;  InsertList(LL, 1, &ee);ee=3;  InsertList(LL, 1, &ee);ee=4;  InsertList(LL, 1, &ee);ee=5;  InsertList(LL, 1, &ee);ee=6;  InsertList(LL, 1, &ee);ee=7;  InsertList(LL, 1, &ee);ee=8;  InsertList(LL, 1, &ee);ee=9;  InsertList(LL, 1, &ee);ee=10; InsertList(LL, 1, &ee);printf("length=%d\n",LengthList(LL));PrintList(LL);printf("在第5个位置插入元素(13)。\n");ee=13; InsertList(LL, 5, &ee);  PrintList(LL);printf("在表头插入元素(11),表尾插入元素(12)。\n");ee=11; PushFront(LL, &ee);ee=12; PushBack(LL, &ee);PrintList(LL);printf("删除表中第7个结点。\n");DeleteNode(LL,7); PrintList(LL);printf("删除表中第一个结点。\n");PopFront(LL); PrintList(LL);printf("删除表中最后一个结点。\n");PopBack(LL); PrintList(LL);LNode *tmp;if ( (tmp=LocateNode(LL,3)) != NULL)printf("第3个结点的地址是=%p,ee=%d\n",tmp,tmp->data);ee=8;if ( (tmp=LocateElem(LL,&ee)) != NULL)printf("元素值为8的结点的地址是=%p\n",tmp);elseprintf("元素值为8的结点的地址是NULL,没找着。\n");printf("在结点%p之后插入66\n",tmp);ee=66;InsertNextNode(tmp,&ee);  PrintList(LL);printf("在结点%p之前插入55\n",tmp);ee=55;InsertPriorNode(tmp,&ee);  PrintList(LL);// ReverseList(LL); PrintList(LL);  // 反转链表。DestroyList1(LL); LL=NULL;  // 销毁链表,LL置为空。// DestroyList2(&LL);       // 销毁链表,传入指针的地址,LL在函数中会置为空。// DestroyList3(LL);       // 销毁链表,C++的引用,LL在函数中会置为空。printf("LL=%p\n",LL);/*// 以下代码显示两个链表的合并,把两个有序的La和Lb合并成有序的Lc。LinkList La,Lb,Lc;La=InitList1();Lb=InitList1();Lc=InitList1();ee=1;  PushBack(Lb, &ee);ee=2;  PushBack(La, &ee);ee=3;  PushBack(Lb, &ee);ee=4;  PushBack(Lb, &ee);ee=5;  PushBack(La, &ee);ee=6;  PushBack(Lb, &ee);ee=7;  PushBack(Lb, &ee);ee=8;  PushBack(Lb, &ee);ee=9;  PushBack(La, &ee);ee=10; PushBack(La, &ee);PrintList(La);PrintList(Lb);MergeList(La,Lb,Lc);PrintList(Lc);DestroyList1(La); La=NULL;DestroyList1(Lb); Lb=NULL;DestroyList1(Lc); Lc=NULL;*/return 0;
}// 如果参数采用转指针LL的值,LL的值只能传进去,无法返回,这种方法是不行的。
int InitList4(LinkList LL)
{LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return 0;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。LL=head;printf("LL1=%p\n",LL);return 1;
}// 初始化链表LL,返回值:失败返回NULL,成功返回头结点的地址。
LNode *InitList1()
{LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return NULL;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。return head;
}// 传入指针变量的地址的方法。
// 初始化链表,返回值:0-失败;1-成功。
int InitList2(LinkList *LL)
{// 在本函数中,LL是指针的指针,用于存放指针的地址。if ( *LL != NULL ) { printf("链表LL已存在,在初始化之前请先释放它。\n"); return 0; }LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return 0;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。*LL=head;return 1;
}/*
// C++引用的方法。
// 初始化链表,返回值:0-失败;1-成功。
int InitList3(LinkList &LL)
{if ( LL != NULL ) { printf("链表L已存在,在初始化之前请先释放它。\n"); return 0; }LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return 0;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。LL=head;return 1;
}
*/// 销毁链表LL。
void DestroyList1(LinkList LL)
{// 销毁链表LL是指释放链表全部的结点,包括头结点。LNode *tmp;while(LL!=NULL){tmp=LL->next;  // tmp保存下一结点的地址。free(LL);      // 释放当前结点。LL=tmp;        // LL指针移动到下一结点。}// LL=NULL;   // LL在本函数中相当于局部变量,就算置空了也不会影响调用者传递的LL,所以LL=NULL没有意义。return;
}// 销毁链表LL。
void DestroyList2(LinkList *LL)
{  // 如果函数的参数是指针的指针,可以启用以下代码。LNode *tmp1,*tmp2;tmp1=*LL;while(tmp1!=NULL){tmp2=tmp1->next; // tmp保存下一结点的地址。free(tmp1);      // 释放当前结点。tmp1=tmp2;       // LL指针移动到下一结点。}*LL=NULL;  // 把链表的指针置为空,表示链表不存在了。return;
}/*
// C++引用的方法。
// 传入指针的地址的方法。
void DestroyList3(LinkList &LL)
{// 销毁链表LL是指释放链表全部的结点,包括头结点。LNode *tmp;while(LL!=NULL){tmp=LL->next;   // tmp保存下一结点的地址。free(LL);       // 释放当前结点。LL=tmp;         // LL指针移动到下一结点。}LL=NULL;  // 把链表的指针置为空,表示链表不存在了。return;
}
*/// 清空链表。
void ClearList(LinkList LL)
{// 清空链表LL是指释放链表全部的结点,但不包括头结点。if (LL == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。LNode *tmp1;LNode *tmp2=LL->next;  // 保留头结点,从头结点的下一个结点开始释放。while(tmp2!=NULL){tmp1=tmp2->next;free(tmp2);tmp2=tmp1;}LL->next=NULL; // 这行代码一定不能少,否则会留下野指针。return;
}// 在链表LL的第ii个位置插入元素ee,返回值:0-失败;1-成功。
int InsertList(LinkList LL, unsigned int ii, ElemType *ee)
{if ( (LL == NULL) || (ee == NULL) ) { printf("链表LL或元素ee不存在。\n"); return 0; } // 判断表和元素是否存在。// 判断插入位置是否合法if (ii < 1) { printf("插入位置(%d)不合法,应该在大于0。\n",ii); return 0; }// 要在位序ii插入结点,必须找到ii-1结点。LNode *pp=LL;  // 指针pp指向头结点,逐步往后移动,如果为空,表示后面没结点了。int kk=0;      // kk指向的是第几个结点,从头结点0开始,pp每向后移动一次,kk就加1。while ( (pp != NULL) && (kk < ii-1) ){pp=pp->next; kk++;// printf("pp=%p,kk=%d\n",pp,kk);}if ( pp==NULL ) { printf("位置(%d)不合法,超过了表长。\n",ii); return 0; }LNode *tmp = (LNode *)malloc(sizeof(LNode));  // 分配一个结点。if (tmp == NULL) return 0;  // 内存不足,返回失败。// 考虑数据元素为结构体的情况,这里采用了memcpy的方法而不是直接赋值。memcpy(&tmp->data,ee,sizeof(ElemType));// 处理next指针。tmp->next=pp->next;pp->next=tmp;return 1;///// 以上代码可以用以下代码代替。// LNode *pp=LocateNode(LL,ii-1);  // return InsertNextNode(pp,ee);///
}// 删除链表LL中的第ii个结点,返回值:0-位置ii不合法;1-成功。
int  DeleteNode(LinkList LL, unsigned int ii)
{if (LL == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。// 判断删除位置是否合法if (ii < 1) { printf("删除位置(%d)不合法,应该在大于0。\n",ii); return 0; }// 要删除位序ii结点,必须找到ii-1结点。LNode *pp=LL;  // 指针pp指向头结点,逐步往后移动,如果为空,表示后面没结点了。int kk=0;      // kk指向的是第几个结点,从头结点0开始,pp每向后移动一次,kk就加1。while ( (pp != NULL) && (kk < ii-1) ){pp=pp->next; kk++;}// 注意,以下行的代码与视频中的不一样,视频中的是 if ( pp==NULL ),有bug。if ( pp->next==NULL ) { printf("位置(%d)不合法,超过了表长。\n",ii); return 0; }LNode *tmp=pp->next;  // tmp为将要删除的结点。pp->next=pp->next->next;   // 写成p->next=tmp->next更简洁。free(tmp);return 1;
}// 在链表LL的头部插入元素ee,返回值:0-失败;1-成功。
int  PushFront(LinkList LL, ElemType *ee)
{return InsertList(LL,1,ee);
}// 在链表LL的尾部插入元素ee,返回值:0-失败;1-成功。
int PushBack(LinkList LL, ElemType *ee)
{if ( (LL == NULL) || (ee == NULL) ) { printf("链表LL或元素ee不存在。\n"); return 0; } // 判断表和元素是否存在。LNode *pp=LL;  // 从头结点开始。// 找到最后一个结点。while (pp->next != NULL) pp=pp->next;LNode *tmp = (LNode *)malloc(sizeof(LNode));  // 分配一个结点。if (tmp == NULL) return 0;  // 内存不足,返回失败。// 考虑数据元素为结构体的情况,这里采用了memcpy的方法而不是直接赋值。memcpy(&tmp->data,ee,sizeof(ElemType));// 处理next指针。tmp->next=NULL;pp->next=tmp;return 1;
}// 删除链表LL中第一个结点,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL)
{return DeleteNode(LL, 1);
}// 删除链表LL中最后一个结点,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL)
{if ( LL == NULL ) { printf("链表LL不存在。\n"); return 0; } // 判断表和元素是否存在。// 必须加上这个判断,否则下面的循环pp->next->next不成立。if ( LL->next == NULL) { printf("链表LL为空,没有尾结点。\n"); return 0; } // 判断表是否为空。// 要删除最后一个结点,必须找到最后一个结点的前一个结点。LNode *pp=LL;  // 从第0个结点开始。// 找到倒数第二个结点(包括头结点)。while (pp->next->next != NULL) pp=pp->next;// 释放最后一个结点。free(pp->next);pp->next=NULL;return 1;
}// 打印链表中全部的元素。
void PrintList(LinkList LL)
{if (LL == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。LNode *pp=LL->next;  // 从第1个结点开始。while (pp != NULL){printf("%-3d", pp->data);  // 如果元素ee为结构体,这行代码要修改。pp=pp->next;}printf("\n");/*// 以下代码用于显示全部结点的地址和元素的值。LNode *pp=LL;  // 从第0个结点开始。while (pp != NULL){printf("%p,%p,%-3d\n",pp,pp->next,pp->data);  // 如果元素ee为结构体,这行代码要修改。pp=pp->next;}*/
}// 求链表的长度,返回值:>=0-表LL结点的个数。
int  LengthList(LinkList LL)
{if (LL == NULL) { printf("链表LL不存在。\n"); return 0; } // 判断链表是否存在。LNode *pp=LL->next;  // 头结点不算,从第1个结点开始。int length=0;while (pp != NULL) { pp=pp->next; length++; }return length;// 不使用临时变量,如何计算链表(包括头结点)的长度?// if (LL==NULL) return 0;// return LengthList(LL->next)+1;
}// 判断链表是否为空,返回值:0-非空或失败,1-空。
int IsEmpty(LinkList LL)
{if (LL == NULL) return 0;if (LL->next == NULL) return 1;return 0;
}// 获取链表中第ii个结点,成功返回结点的地址,失败返回空。
// 注意,ii可以取值为0,表示头结点。
LNode *LocateNode(LinkList LL, unsigned int ii)
{if ( LL == NULL ) { printf("链表LL不存在。\n"); return NULL; } // 判断表和元素是否存在。LNode *pp=LL;  // 指针pp指向头结点,逐步往后移动,如果为空,表示后面没结点了。int kk=0;      // kk指向的是第几个结点,从头结点0开始,pp每向后移动一次,kk就加1。while ( (pp != NULL) && (kk < ii) ){ pp=pp->next; kk++; }if ( pp==NULL ) { printf("位置(%d)不合法,超过了表长。\n",ii); return NULL; }return pp;
}// 查找元素ee在链表LL中的结点地址,如果没找到返回NULL,否则返回结点的地址。
LNode *LocateElem(LinkList LL, ElemType *ee)
{LNode *pp=LL->next;  // 从第1个数据结点开始。while (pp != NULL){// 如果数据元素是结构体,以下代码要修改。if (pp->data == *ee) return pp;pp = pp->next;}return NULL;
}// 采用归并的方法,将两个升序的链表La和Lb,合并成一个升序的链表Lc。
int MergeList(LinkList La,LinkList Lb,LinkList Lc)
{if ( (La == NULL) || (Lb == NULL) || (Lc == NULL) ) { printf("表La、Lb、Lc至少有一个不存在。\n"); return 0; }La=La->next;Lb=Lb->next;LNode *pp;// 把La和Lb合并到Lc中。while ( (La != NULL) && (Lb != NULL) ){// 取La和Lb的较小者。if (La->data <= Lb->data){pp=La; La=La->next;}else{pp=Lb; Lb=Lb->next;}// 把较小者追加到Lc中。Lc->next=(LNode *)malloc(sizeof(LNode));  // 分配一个新结点。Lc=Lc->next;memcpy(&Lc->data,&pp->data,sizeof(ElemType));Lc->next=NULL;}// 把链表La其它的元素追加到Lc中。while (La != NULL){Lc->next=(LNode *)malloc(sizeof(LNode));  // 分配一个新结点。Lc=Lc->next;memcpy(&Lc->data,&La->data,sizeof(ElemType));Lc->next=NULL;La=La->next;}// 把链表Lb其它的元素追加到Lc中。while (Lb != NULL){Lc->next=(LNode *)malloc(sizeof(LNode));  // 分配一个新结点。Lc=Lc->next;memcpy(&Lc->data,&Lb->data,sizeof(ElemType));Lc->next=NULL;Lb=Lb->next;}return 1;
}// 在指定结点pp之后插入元素ee,返回值:0-失败;1-成功。
int InsertNextNode(LNode *pp, ElemType *ee)
{if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }LNode *tmp = (LNode *)malloc(sizeof(LNode));if (tmp == NULL) return 0;memcpy(&tmp->data,ee,sizeof(ElemType));tmp->next=pp->next;pp->next=tmp;return 1;
}// 在指定结点pp之前插入元素ee,返回值:0-失败;1-成功。
int InsertPriorNode(LNode *pp, ElemType *ee)
{if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }// 在指定结点pp之前插入采用偷梁换柱的方法:// 1、分配一个新的结点;// 2、把pp结点的数据和指针复制到新结点中。// 3、把待插入元素的数据存入pp结点中。LNode *tmp = (LNode *)malloc(sizeof(LNode));if (tmp == NULL) return 0;// 把pp结点的数据和指针复制到tmp中。memcpy(&tmp->data,&pp->data,sizeof(ElemType));tmp->next=pp->next;// 把待插入的元素存入pp中。memcpy(&pp->data,ee,sizeof(ElemType));pp->next=tmp;return 1;
}// 删除指定结点。
int DeleteNode1(LNode *pp)
{if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }// 删除指定结点的思想是:1)把pp后继结点的数据和next指针复制到pp结点;2)删除pp结点的后继结点。LNode *tmp=pp->next;  // tmp指向pp的后继结点。memcpy(&pp->data,&tmp->data,sizeof(ElemType)); // 把后继结点的数据复制到pp结点中。pp->next=tmp->next;   // 把pp的next指向后继结点的next。free(tmp);  // 释放后继结点。// 写这个函数的目的是告诉大家这种方法是有问题的。// 问题:如果当前的pp结点是链表的最后一个结点,那么它的后继结点根本不存在。// 结论:此法不通,还是乖乖的从链表头部开始扫描。return 1;
}// 把链表pp结点之后的结点原地逆置(反转),返回值:0-失败;1-成功。
void ReverseList(LNode *pp)
{LNode *ss;      // 当前结点。LNode *ssnext;  // 当前结点的下一结点。ss=pp->next;    // 从pp结点之后的结点开始反转。pp->next=NULL;  // pp->next指向空。while (ss != NULL){ssnext=ss->next;  // 保留ss下一结点的地址。// 以下两行相当于在pp之后插入ss结点。ss->next=pp->next;  pp->next=ss;ss=ssnext;  // ss结点后移。}
}
/** 程序名:linklist2.c,此程序演示带头结点的单链表的实现,数据元素是结构体。*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>typedef struct
{int  no;        // 超女编号。char name[31];  // 超女姓名。
}ElemType;        // 自定义顺序表的数据元素为结构体。typedef struct LNode
{ElemType data;       // 存放结点的数据元素。struct LNode *next;  // 指向下一个结点的指针。
}LNode,*LinkList;// 初始化链表LL,返回值:失败返回NULL,成功返回头结点的地址。
LNode *InitList1();// 传入指针变量的地址的方法。
// 初始化链表,返回值:0-失败;1-成功。
int InitList2(LinkList *LL);// C++引用的方法,在Linux下,需要用g++编译。
// 初始化链表,返回值:0-失败;1-成功。
// int InitList3(LinkList &LL);// 如果参数采用转指针LL的值,LL的值只能传进去,无法返回,这种方法是不行的。
int InitList4(LinkList LL);// 销毁链表LL。
void DestroyList1(LinkList LL);// 销毁链表LL。
// 传入指针的地址的方法。
void DestroyList2(LinkList *LL);// C++引用的方法,在Linux下,需要用g++编译。
// 传入指针的地址的方法。
// void DestroyList3(LinkList &LL);// 清空链表。
void ClearList(LinkList LL);                    // 在链表LL的第ii个位置插入元素ee,返回值:0-失败;1-成功。
int  InsertList(LinkList LL, unsigned int ii, ElemType *ee);   // 打印链表中全部的元素。
void PrintList(LinkList LL);                    // 打印链表中全部的元素,包括超女编号和姓名。
void PrintList1(LinkList LL);// 在链表LL的头部插入元素ee,返回值:0-失败;1-成功。
int  PushFront(LinkList LL, ElemType *ee);// 在链表LL的尾部插入元素ee,返回值:0-失败;1-成功。
int  PushBack(LinkList LL, ElemType *ee);// 删除链表LL中的第ii个结点,返回值:0-位置ii不合法;1-成功。
int  DeleteNode(LinkList LL, unsigned int ii);  // 删除链表LL中第一个结点,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL);// 删除链表LL中最后一个结点,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL);// 求链表的长度,返回值:>=0-表LL结点的个数。
int  LengthList(LinkList LL);                   // 判断链表是否为空,返回值:0-非空或失败,1-空。
int IsEmpty(LinkList LL);// 获取链表中第ii个结点,成功返回结点的地址,失败返回空。
// 注意,ii可以取值为0,表示头结点。
LNode *LocateNode(LinkList LL, unsigned int ii);// 查找元素ee在链表LL中的结点地址,如果没找到返回NULL,否则返回结点的地址。
LNode *LocateElem(LinkList LL, ElemType *ee);// 在指定结点pp之后插入元素ee,返回值:0-失败;1-成功。
int InsertNextNode(LNode *pp, ElemType *ee);// 在指定结点pp之前插入元素ee,返回值:0-失败;1-成功。
int InsertPriorNode(LNode *pp, ElemType *ee);// 删除指定结点。
int DeleteNode1(LNode *pp);int main()
{LinkList LL=NULL; // 声明链表指针变量。LL=InitList1();     // 初始化链表。// 如果要在函数中对变量进行赋值,必须把变量的地址传入函数。// 指针变量简称指针,如果要在函数中对指针变量赋值,也必须把指针的地址传入函数。// LL是指针,在InitList2函数中,需要把头结点的地址赋值给LL,所以要传入LL的地址。// 指针是变量,用于存放变量的地址,指针不是地址,指针里存放的内容才是地址。// 所以,这里要把指针变量LL的地址传给InitList2()函数。// 各位菜鸡,明白鸟吗?// InitList2(&LL);     // 初始化链表,传入指针变量LL的地址。// InitList3(LL);      // 初始化链表,C++的引用。// 如果参数采用转指针LL的值,LL的值只能传进去,无法返回,这种方法是不行的。// InitList4(LL);printf("LL=%p\n",LL);ElemType ee;      // 创建一个数据元素。printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");ee.no=1;  strcpy(ee.name,"妲己");   InsertList(LL, 1, &ee);ee.no=2;  strcpy(ee.name,"褒姒");   InsertList(LL, 1, &ee);ee.no=3;  strcpy(ee.name,"西施");   InsertList(LL, 1, &ee);ee.no=4;  strcpy(ee.name,"王昭君"); InsertList(LL, 1, &ee);ee.no=5;  strcpy(ee.name,"貂蝉");   InsertList(LL, 1, &ee);ee.no=6;  strcpy(ee.name,"杨玉环"); InsertList(LL, 1, &ee);ee.no=7;  strcpy(ee.name,"赵飞燕"); InsertList(LL, 1, &ee);ee.no=8;  strcpy(ee.name,"萧美娘"); InsertList(LL, 1, &ee);ee.no=9;  strcpy(ee.name,"陈圆圆"); InsertList(LL, 1, &ee);ee.no=10; strcpy(ee.name,"李师师"); InsertList(LL, 1, &ee);printf("length=%d\n",LengthList(LL));PrintList(LL);printf("在第5个位置插入元素(13)。\n");memset(&ee,0,sizeof(ElemType)); ee.no=13; InsertList(LL, 5, &ee);  PrintList(LL);printf("在表头插入元素(11),表尾插入元素(12)。\n");memset(&ee,0,sizeof(ElemType)); ee.no=11; PushFront(LL, &ee);memset(&ee,0,sizeof(ElemType)); ee.no=12; PushBack(LL, &ee);PrintList(LL);printf("删除表中第7个结点。\n");DeleteNode(LL,7); PrintList(LL);printf("删除表中第一个结点。\n");PopFront(LL); PrintList(LL);printf("删除表中最后一个结点。\n");PopBack(LL); PrintList(LL);LNode *tmp;if ( (tmp=LocateNode(LL,3)) != NULL)printf("第3个结点的地址是=%p,ee.no=%d\n",tmp,tmp->data.no);memset(&ee,0,sizeof(ElemType)); ee.no=8;if ( (tmp=LocateElem(LL,&ee)) != NULL)printf("元素值为8的结点的地址是=%p\n",tmp);elseprintf("元素值为8的结点的地址是NULL,没找着。\n");printf("在结点%p之后插入66\n",tmp);memset(&ee,0,sizeof(ElemType)); ee.no=66;InsertNextNode(tmp,&ee);  PrintList(LL);printf("在结点%p之前插入55\n",tmp);memset(&ee,0,sizeof(ElemType)); ee.no=55;InsertPriorNode(tmp,&ee);  PrintList(LL);PrintList1(LL);DestroyList1(LL); LL=NULL;  // 销毁链表,LL置为空。// DestroyList2(&LL);       // 销毁链表,传入指针的地址,LL在函数中会置为空。// DestroyList3(LL);       // 销毁链表,C++的引用,LL在函数中会置为空。printf("LL=%p\n",LL);return 0;
}// 如果参数采用转指针LL的值,LL的值只能传进去,无法返回,这种方法是不行的。
int InitList4(LinkList LL)
{LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return 0;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。LL=head;printf("LL1=%p\n",LL);return 1;
}// 初始化链表LL,返回值:失败返回NULL,成功返回头结点的地址。
LNode *InitList1()
{LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return NULL;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。return head;
}// 传入指针变量的地址的方法。
// 初始化链表,返回值:0-失败;1-成功。
int InitList2(LinkList *LL)
{// 在本函数中,LL是指针的指针,用于存放指针的地址。if ( *LL != NULL ) { printf("链表LL已存在,在初始化之前请先释放它。\n"); return 0; }LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return 0;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。*LL=head;return 1;
}/*
// C++引用的方法。
// 初始化链表,返回值:0-失败;1-成功。
int InitList3(LinkList &LL)
{if ( LL != NULL ) { printf("链表L已存在,在初始化之前请先释放它。\n"); return 0; }LNode *head = (LNode *)malloc(sizeof(LNode));  // 分配头结点。if (head == NULL) return 0;  // 内存不足,返回失败。head->next=NULL;  // 头结点的下一结点暂时不存在,置空。LL=head;return 1;
}
*/// 销毁链表LL。
void DestroyList1(LinkList LL)
{// 销毁链表LL是指释放链表全部的结点,包括头结点。LNode *tmp;while(LL!=NULL){tmp=LL->next;  // tmp保存下一结点的地址。free(LL);      // 释放当前结点。LL=tmp;        // LL指针移动到下一结点。}// LL=NULL;   // LL在本函数中相当于局部变量,就算置空了也不会影响调用者传递的LL,所以LL=NULL没有意义。return;
}// 销毁链表LL。
void DestroyList2(LinkList *LL)
{  // 如果函数的参数是指针的指针,可以启用以下代码。LNode *tmp1,*tmp2;tmp1=*LL;while(tmp1!=NULL){tmp2=tmp1->next; // tmp保存下一结点的地址。free(tmp1);      // 释放当前结点。tmp1=tmp2;       // LL指针移动到下一结点。}*LL=NULL;  // 把链表的指针置为空,表示链表不存在了。return;
}/*
// C++引用的方法。
// 传入指针的地址的方法。
void DestroyList3(LinkList &LL)
{// 销毁链表LL是指释放链表全部的结点,包括头结点。LNode *tmp;while(LL!=NULL){tmp=LL->next;   // tmp保存下一结点的地址。free(LL);       // 释放当前结点。LL=tmp;         // LL指针移动到下一结点。}LL=NULL;  // 把链表的指针置为空,表示链表不存在了。return;
}
*/// 清空链表。
void ClearList(LinkList LL)
{// 清空链表LL是指释放链表全部的结点,但不包括头结点。if (LL == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。LNode *tmp1;LNode *tmp2=LL->next;  // 保留头结点,从头结点的下一个结点开始释放。while(tmp2!=NULL){tmp1=tmp2->next;free(tmp2);tmp2=tmp1;}LL->next=NULL; // 这行代码一定不能少,否则会留下野指针。return;
}// 在链表LL的第ii个位置插入元素ee,返回值:0-失败;1-成功。
int InsertList(LinkList LL, unsigned int ii, ElemType *ee)
{if ( (LL == NULL) || (ee == NULL) ) { printf("链表LL或元素ee不存在。\n"); return 0; } // 判断表和元素是否存在。// 判断插入位置是否合法if (ii < 1) { printf("插入位置(%d)不合法,应该在大于0。\n",ii); return 0; }// 要在位序ii插入结点,必须找到ii-1结点。LNode *pp=LL;  // 指针pp指向头结点,逐步往后移动,如果为空,表示后面没结点了。int kk=0;      // kk指向的是第几个结点,从头结点0开始,pp每向后移动一次,kk就加1。while ( (pp != NULL) && (kk < ii-1) ){pp=pp->next; kk++;// printf("pp=%p,kk=%d\n",pp,kk);}if ( pp==NULL ) { printf("位置(%d)不合法,超过了表长。\n",ii); return 0; }LNode *tmp = (LNode *)malloc(sizeof(LNode));  // 分配一个结点。if (tmp == NULL) return 0;  // 内存不足,返回失败。// 考虑数据元素为结构体的情况,这里采用了memcpy的方法而不是直接赋值。memcpy(&tmp->data,ee,sizeof(ElemType));// 处理next指针。tmp->next=pp->next;pp->next=tmp;return 1;///// 以上代码可以用以下代码代替。// LNode *pp=LocateNode(LL,ii-1);  // return InsertNextNode(pp,ee);///
}// 删除链表LL中的第ii个结点,返回值:0-位置ii不合法;1-成功。
int  DeleteNode(LinkList LL, unsigned int ii)
{if (LL == NULL) { printf("链表L不存在。\n"); return 0; } // 判断链表是否存在。// 判断删除位置是否合法if (ii < 1) { printf("删除位置(%d)不合法,应该在大于0。\n",ii); return 0; }// 要删除位序ii结点,必须找到ii-1结点。LNode *pp=LL;  // 指针pp指向头结点,逐步往后移动,如果为空,表示后面没结点了。int kk=0;      // kk指向的是第几个结点,从头结点0开始,pp每向后移动一次,kk就加1。while ( (pp != NULL) && (kk < ii-1) ){pp=pp->next; kk++;}// 注意,以下行的代码与视频中的不一样,视频中的是 if ( pp==NULL ),有bug。if ( pp->next==NULL ) { printf("位置(%d)不合法,超过了表长。\n",ii); return 0; }LNode *tmp=pp->next;   // tmp为将要删除的结点。pp->next=pp->next->next;    // 写成p->next=tmp->next更简洁。free(tmp);return 1;
}// 在链表LL的头部插入元素ee,返回值:0-失败;1-成功。
int  PushFront(LinkList LL, ElemType *ee)
{return InsertList(LL,1,ee);
}// 在链表LL的尾部插入元素ee,返回值:0-失败;1-成功。
int PushBack(LinkList LL, ElemType *ee)
{if ( (LL == NULL) || (ee == NULL) ) { printf("链表LL或元素ee不存在。\n"); return 0; } // 判断表和元素是否存在。LNode *pp=LL;  // 从头结点开始。// 找到最后一个结点。while (pp->next != NULL) pp=pp->next;LNode *tmp = (LNode *)malloc(sizeof(LNode));  // 分配一个结点。if (tmp == NULL) return 0;  // 内存不足,返回失败。// 考虑数据元素为结构体的情况,这里采用了memcpy的方法而不是直接赋值。memcpy(&tmp->data,ee,sizeof(ElemType));// 处理next指针。tmp->next=NULL;pp->next=tmp;return 1;
}// 删除链表LL中第一个结点,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL)
{return DeleteNode(LL, 1);
}// 删除链表LL中最后一个结点,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL)
{if ( LL == NULL ) { printf("链表LL不存在。\n"); return 0; } // 判断表和元素是否存在。// 必须加上这个判断,否则下面的循环pp->next->next不成立。if ( LL->next == NULL) { printf("链表LL为空,没有尾结点。\n"); return 0; } // 判断表是否为空。// 要删除最后一个结点,必须找到最后一个结点的前一个结点。LNode *pp=LL;  // 从第0个结点开始。// 找到倒数第二个结点(包括头结点)。while (pp->next->next != NULL) pp=pp->next;// 释放最后一个结点。free(pp->next);pp->next=NULL;return 1;
}// 打印链表中全部的元素。
void PrintList(LinkList LL)
{if (LL == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。LNode *pp=LL->next;  // 从第1个结点开始。while (pp != NULL){printf("%-3d", pp->data.no);  pp=pp->next;}printf("\n");/*// 以下代码用于显示全部结点的地址和元素的值。LNode *pp=LL;  // 从第0个结点开始。while (pp != NULL){printf("%p,%p,%-3d\n",pp,pp->next,pp->data.no);  pp=pp->next;}*/
}// 打印链表中全部的元素,包括超女编号和姓名。
void PrintList1(LinkList LL)
{if (LL == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。LNode *pp=LL->next;  // 从第1个结点开始。while (pp != NULL){printf("no=%d,name=%s\n",pp->data.no,pp->data.name);  pp=pp->next;}
}// 求链表的长度,返回值:>=0-表LL结点的个数。
int  LengthList(LinkList LL)
{if (LL == NULL) { printf("链表LL不存在。\n"); return 0; } // 判断链表是否存在。LNode *pp=LL->next;  // 头结点不算,从第1个结点开始。int length=0;while (pp != NULL) { pp=pp->next; length++; }return length;// 不使用临时变量,如何计算链表(包括头结点)的长度?// if (LL==NULL) return 0;// return LengthList(LL->next)+1;
}// 判断链表是否为空,返回值:0-非空或失败,1-空。
int IsEmpty(LinkList LL)
{if (LL == NULL) return 0;if (LL->next == NULL) return 1;return 0;
}// 获取链表中第ii个结点,成功返回结点的地址,失败返回空。
// 注意,ii可以取值为0,表示头结点。
LNode *LocateNode(LinkList LL, unsigned int ii)
{if ( LL == NULL ) { printf("链表LL不存在。\n"); return NULL; } // 判断表和元素是否存在。LNode *pp=LL;  // 指针pp指向头结点,逐步往后移动,如果为空,表示后面没结点了。int kk=0;      // kk指向的是第几个结点,从头结点0开始,pp每向后移动一次,kk就加1。while ( (pp != NULL) && (kk < ii) ){ pp=pp->next; kk++; }if ( pp==NULL ) { printf("位置(%d)不合法,超过了表长。\n",ii); return NULL; }return pp;
}// 查找元素ee在链表LL中的结点地址,如果没找到返回NULL,否则返回结点的地址。
LNode *LocateElem(LinkList LL, ElemType *ee)
{LNode *pp=LL->next;  // 从第1个数据结点开始。while (pp != NULL){if (pp->data.no == ee->no) return pp;pp = pp->next;}return NULL;
}// 在指定结点pp之后插入元素ee,返回值:0-失败;1-成功。
int InsertNextNode(LNode *pp, ElemType *ee)
{if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }LNode *tmp = (LNode *)malloc(sizeof(LNode));if (tmp == NULL) return 0;memcpy(&tmp->data,ee,sizeof(ElemType));tmp->next=pp->next;pp->next=tmp;return 1;
}// 在指定结点pp之前插入元素ee,返回值:0-失败;1-成功。
int InsertPriorNode(LNode *pp, ElemType *ee)
{if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }// 在指定结点pp之前插入采用偷梁换柱的方法:// 1、分配一个新的结点;// 2、把pp结点的数据和指针复制到新结点中。// 3、把待插入元素的数据存入pp结点中。LNode *tmp = (LNode *)malloc(sizeof(LNode));if (tmp == NULL) return 0;// 把pp结点的数据和指针复制到tmp中。memcpy(&tmp->data,&pp->data,sizeof(ElemType));tmp->next=pp->next;// 把待插入的元素存入pp中。memcpy(&pp->data,ee,sizeof(ElemType));pp->next=tmp;return 1;
}// 删除指定结点。
int DeleteNode1(LNode *pp)
{if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }// 删除指定结点的思想是:1)把pp后继结点的数据和next指针复制到pp结点;2)删除pp结点的后继结点。LNode *tmp=pp->next;  // tmp指向pp的后继结点。memcpy(&pp->data,&tmp->data,sizeof(ElemType)); // 把后继结点的数据复制到pp结点中。pp->next=tmp->next;   // 把pp的next指向后继结点的next。free(tmp);  // 释放后继结点。// 写这个函数的目的是告诉大家这种方法是有问题的。// 问题:如果当前的pp结点是链表的最后一个结点,那么它的后继结点根本不存在。// 结论:此法不通,还是乖乖的从链表头部开始扫描。return 1;
}

数据结构:单链表包含(归并)相关推荐

  1. 小白算法积累——单链表13#带头结点单链表+就地归并+改序

    题目:假设有两个按元素值递增次序排列的线性表,均以单链表的形式存储.请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表. 关键字:带头结点 ...

  2. 数据结构-单链表基本操作(C语言实现)

    参考书:王道考研数据结构 (此贴为博主学习408的笔记,因博主也是学习者,个人总结如有错误欢迎指正.如有侵权请告知,马上删除致歉)​​ 单链表定义 单链表是线性表的链式存储,通过一组任意的存储单元来存 ...

  3. 20175330 数据结构-单链表(选做)

    要求 参见附件,补充MyList.java的内容,提交运行结果截图(全屏) 课下推送代码到码云 ``` public class MyList {     public static void mai ...

  4. php链表和联表的区别,PHP_浅谈PHP链表数据结构(单链表),链表:是一个有序的列表,但 - phpStudy...

    浅谈PHP链表数据结构(单链表) 链表:是一个有序的列表,但是它在内存中是分散存储的,使用链表可以解决类似约瑟夫问题,排序问题,搜索问题,广义表 单向链表,双向链表,环形链表 PHP的底层是C,当一个 ...

  5. 数据结构——单链表的C++实现

    数据结构--单链表的C++实现 \qquad单链表的创建.求长度.查找.插入和删除的C++实现. #include<iostream> using namespace std;//1.定义 ...

  6. php mysql 链表_浅谈PHP链表数据结构(单链表)

    链表:是一个有序的列表,但是它在内存中是分散存储的,使用链表可以解决类似约瑟夫问题,排序问题,搜索问题,广义表 单向链表,双向链表,环形链表 PHP的底层是C,当一个程序运行时,内存分成五个区(堆区, ...

  7. python 单链表是否有回路_(Python3)数据结构--单链表之判断链表是否有环

    前言 有Python基础 有数据结构单链表基础,没接触过的可以看下面链接 https://blog.csdn.net/sf9898/article/details/104946291 原理和实现 有一 ...

  8. 单链表的归并算法思路总结

    刚在练习的时候需要将两个递增有序的单链表进行归并处理,之前碰到这种问题,心里总是有些害怕,害怕自己不能完全考虑到所有的情况,怕自己想不明白里面的流程,怕自己做不到... 但是,我慢慢理解并深以为然的是 ...

  9. 数据结构 —— 单链表(超详细图解 接口函数实现)

    系列文章目录 数据结构 -- 顺序表 数据结构 -- 单链表 数据结构 -- 双向链表 数据结构 -- 队列 数据结构 -- 栈 数据结构 -- 堆 数据结构 -- 二叉树 数据结构 -- 八大排序 ...

  10. C语言数据结构单链表链表

    数据结构–单链表 学习了顺序表,我们发现顺序表在向里面存放数据的时候很麻烦,比如我们要使用头插法存放一个数据到顺序表的时候,我们要将整个表都向后挪一位,这个操作就让人很难受.那么有没有一种结构可以让我 ...

最新文章

  1. 全球Python调查报告:Python 2正在消亡,PyCharm比VS Code更受欢迎
  2. iOS开发 QQ粘性动画效果
  3. 成功解决TypeError: map() got an unexpected keyword argument 'num_threads'
  4. 电脑屏幕变黄如何调整_如何调整电脑屏幕比例
  5. CISSP的成长之路(十六):复习访问控制(1)
  6. miniui展示日历能点击_2020年日历设计,除了366天有新字体,还有新形式
  7. 以腾讯云IoT Suite为例 谈谈边缘计算在物联网的实践与实现
  8. Ubuntu 16.04下如何安装QT5?
  9. teststand-介绍
  10. 轻量级高并发物联网服务器接收程序源码
  11. 香港服务器托管单线路、双线路以及多线路如何区别
  12. 保险合同中的“不可抗辩条款”
  13. 拉格朗日乘数和KTT条件
  14. 练习动画最好的方式(二):屏幕指纹开锁动画效果
  15. 国外打工人分享如何如何通过销售excel电子表格赚到 28 万美元
  16. 数据标准化方法z-score讲解(matlab)
  17. MFC禁用编辑框输入法
  18. 晶联讯1353显示屏测试程序
  19. java哥验证德巴赫猜想,C语言验证哥德巴赫猜想
  20. 液晶屏接口协议 MIPI LVDS 概览

热门文章

  1. 非标自动化机械设计行业未来发展趋势
  2. 10-3 德扑,股票演绎法,练习量是个要命的问题
  3. 【AI好书】不想被AI降维打击?美国“四院院士”写的这本深度学习科普书了解一下...
  4. c语言实现MD5计算
  5. 大学计算机基础课后题答案中国铁道出版社,大学计算机基础 铁道出版社 2015年第1版 第7章 习题答案...
  6. 北航2021届计组 - 支持中断的CPU
  7. 单片机的停车场计数系统c51_基于51单片机的停车场车位管理系统
  8. html中url表示方法,HTML网页中的URL表示方式介绍
  9. python 动态仪表盘_利用EXCEL的power pivot+切片器制作动态仪表盘
  10. 脚本循环ping所有网段