链表的代码实现及其讲解

来源:互联网 发布:m1头盔淘宝 编辑:程序博客网 时间:2024/06/06 18:02
第一部分是整体代码,第二部分我会分开进行分析:

第一部分:

#include<stdio.h>#include<iostream>#include<assert.h>typedef int Datatype;typedef struct LinkList{ Datatype data; struct LinkList* next;}List, *pList;pList linshi(Datatype d)//创建一个临时变量{ pList tmp = NULL; tmp = (pList)malloc(sizeof(List)); if (tmp == NULL) {  perror("malloc()");  exit(EXIT_FAILURE); } tmp->data = d; tmp->next = NULL;}void InintLinkList(pList *ppList)//初始化链表{ assert(ppList); *ppList = NULL;}void PushFront(pList *pplist, Datatype d)//从前插入节点{ assert(pplist); //空链表 if (*pplist == NULL) {  pList tmp = linshi(d);  *pplist = tmp; } //非空链表 else {  pList cur = *pplist;  pList tmp = linshi(d);  tmp->next = *pplist;  *pplist = tmp; }}void PopFront(pList *pplist)//从前删除节点{ assert(pplist); //非空链表 if (*pplist != NULL) {  pList cur = *pplist;  *pplist = cur->next;  free(cur); } //空链表 else {  return; }}void PushBack(pList *pplist, Datatype d)//从后插入节点{ assert(pplist); //空链表 if (*pplist == NULL) {  pList tmp = linshi(d);  *pplist = tmp;  return; } //非空链表 pList cur = *pplist; pList tmp = linshi(d); while (cur->next != NULL) {  cur = cur->next; } cur->next = tmp;}void PopBack(pList *pplist)//从后删除节点{ assert(pplist); //空链表 if (*pplist == NULL) {  return; } //非空链表 else {  pList cur = *pplist;  cur = cur->next;  if (cur->next == NULL)  {   *pplist = NULL;   free(cur);  }  else  {   pList del = *pplist;   while (cur->next != NULL)   {    del = cur;    cur = cur->next;   }   del->next = NULL;   free(cur);  } }}void Display(pList pplist)//打印链表{ pList cur = pplist; while (cur != NULL) {  printf("%d --> ", cur->data);  cur = cur->next; } printf("over\n");}pList Find(pList pplist, Datatype d)//查询相应数据{ if (pplist == NULL) {  return NULL; } pList cur = pplist; while (cur != NULL) {  if (cur->data == d)  {   return cur;  }  cur = cur->next; } return cur;}void Remove(pList *pplist, Datatype d)//删除{ assert(pplist); if (*pplist == NULL) {  return; } else {  pList cur = *pplist;  while (Find(*pplist, d) != NULL && cur != NULL)  {   if (cur->data == d)   {    pList tmp = cur->next;    cur->data = cur->next->data;    cur->next = cur->next->next;    free(tmp);    return;   }   cur = cur->next;  } }}void RemoveAll(pList *pplist, Datatype d)//删除所有{ assert(pplist); if (*pplist == NULL) {  return; } else {  pList cur = *pplist;  while (Find(*pplist, d) != NULL && cur != NULL)  {   if (cur->data == d)   {    pList tmp = cur->next;    cur->data = cur->next->data;    cur->next = cur->next->next;    free(tmp);   }   cur = cur->next;  } }}void Insert(pList *pplist, int pos, Datatype d)//指定节点插入{ assert(pplist); //空链表 if (*pplist == NULL) {  pList tmp = linshi(d);  *pplist = tmp;  return; } //非空链表 else {  pList cur = *pplist;  pos = pos - 1;  pList tmp = linshi(d);  while (pos--&&cur->next != NULL)  {   cur = cur->next;  }  if (pos > 0)  {   cur->next = tmp;   tmp->data = d;   tmp->next = NULL;   return;  }  tmp->data = cur->data;  cur->data = d;  tmp->next = cur->next;  cur->next = tmp; }}void Insert_Remove(pList *pplist, int pos)//指定节点删除{ assert(pplist); //空链表 if (*pplist == NULL) {  return; } //非空链表 else {  pList cur = *pplist;  pList tmp = *pplist;  pos = pos - 1;  while (pos--&&cur->next != NULL)  {   cur = cur->next;  }  if (pos != 0)  {   pList del = *pplist;   while (tmp->next != NULL)   {    del = tmp;    tmp = tmp->next;   }   del->next = NULL;   free(cur);   return;  }  tmp = cur->next;  cur->data = cur->next->data;  cur->next = cur->next->next;  free(tmp); }}pList Reverse(pList *pplist)//逆序{ assert(pplist); pList cur = *pplist; pList head = cur->next; pList newhead = NULL; head = cur->next; cur->next = NULL; newhead = cur; while (head != NULL) {  cur = head;  head = cur->next;  cur->next = newhead;  newhead = cur; } return newhead;}void Sort(pList *pplist)//排序  从小到大{ assert(pplist); pList cur = *pplist; int count = 0; int i, j; while (cur != NULL) {  count++;  cur = cur->next; } cur = *pplist; for (i = 0; i < count - 1; i++) {  cur = *pplist;  for (j = 0; j < count - 2 - i; j++)  {   if (cur->data > cur->next->data)   {    Datatype tmp;    tmp = cur->data;    cur->data = cur->next->data;    cur->next->data = tmp;   }   cur = cur->next;  } }}int main(){ pList head; InintLinkList(&head); /*PushFront(&head, 1); PushFront(&head, 2); PushFront(&head, 3); PushFront(&head, 4); PushFront(&head, 5); PushFront(&head, 6); Display(head);*/ //PopFront(&head); //PopFront(&head); //PopFront(&head); //Display(head); //PushBack(&head, 1); //PushBack(&head, 2); //PushBack(&head, 7); //PushBack(&head, 4); //PushBack(&head, 7); //PushBack(&head, 6); //PushBack(&head, 7); //PushBack(&head, 8); //PushBack(&head, 9); //Display(head); //PopBack(&head); //PopBack(&head); //PopBack(&head); //pList ret = Find(head, 11); //if (ret == NULL) //{ // printf("没找到!\n"); //} //else //{ // printf("找到了!\n"); //} //Remove(&head, 7); //RemoveAll(&head, 7); //Insert(&head, 2, 11); //Insert_Remove(&head, 13); /*pList tmp = Reverse(&head); Display(tmp);*/ //Sort(&head); Display(head); system("pause"); return 0;}


第二部分:

#include<stdio.h>
#include<assert.h>

(1)引用头文件,因为文章里面用到了断言(assert),所以需要引用assert.h头文件,stdio.h包含了c语言里面一些基本的函数,例如printf  scanf函数等等。

typedef int Datatype;

(2)类型重命名,因为你不知道要创建一个什么类型的链表,所以最好在开头进行一个类型重命名,这样会扩大适用性。

typedef struct LinkList
{
 Datatype data;
 struct LinkList* next;
}List, *pList;

(3)创建一个结构体,作为链表中每个节点的结构类型,并进行了结构体的重命名,结构体为List,结构体指针为pList。

pList linshi(Datatype d)//创建一个临时变量
{
 pList tmp = NULL;
 tmp = (pList)malloc(sizeof(List));
 if (tmp == NULL)
 {
  perror("malloc()");
  exit(EXIT_FAILURE);
 }
 tmp->data = d;
 tmp->next = NULL;
}

(4)因为在函数中会多次应用这段代码,所以我把这段代码单独拿出来分装成一个函数,这样做可以减少代码的长度。

void InintLinkList(pList *ppList)//初始化链表
{
 assert(ppList);
 *ppList = NULL;
}

(5)初始化链表,创建一个新的链表时要进行初始化操作,在后面调用函数时会判断该链表是否为空链表,如果不经心初始化的话,在哦岸段是否为空链表的时候容易出错。

void PushFront(pList *pplist, Datatype d)//从前插入节点
{
 assert(pplist);
 //空链表
 if (*pplist == NULL)
 {
  pList tmp = linshi(d);
  *pplist = tmp;
 }
 //非空链表
 else
 {
  pList cur = *pplist;
  pList tmp = linshi(d);
  tmp->next = *pplist;
  *pplist = tmp;
 }
}

(6)在前面插入节点时,注意要注意保存第一个节点的地址,先把第一个节点的地址赋给新添加节点的next指针,然后head指针再指向新节点,这样就完成了一个节点的添加,下面是图片示意图:
void PopFront(pList *pplist)//从前删除节点
{
 assert(pplist);
 //非空链表
 if (*pplist != NULL)
 {
  pList cur = *pplist;
  *pplist = cur->next;
  free(cur);
 }
 //空链表
 else
 {
  return;
 }
}

(7)从前删除节点只需要用head指针指向第二个节点然后在释放掉第一个节点的空间,因为是动态开辟出来的空间,所以在不用的时候要记住释放掉该空间。

void PushBack(pList *pplist, Datatype d)//从后插入节点
{
 assert(pplist);

 //空链表
 if (*pplist == NULL)
 {
  pList tmp = linshi(d);
  *pplist = tmp;
  return;
 }

 //非空链表
 pList cur = *pplist;
 pList tmp = linshi(d);
 while (cur->next != NULL)
 {
  cur = cur->next;
 }
 cur->next = tmp;
}

(8)在后面添加节点只需要添加一个指针,让他运行到最后一个节点处,然后让其next指针指向该新节点就完成了。
void PopBack(pList *pplist)//从后删除节点
{
 assert(pplist);
 //空链表
 if (*pplist == NULL)
 {
  return;
 }
 //非空链表
 else
 {
  pList cur = *pplist;
  cur = cur->next;
  if (cur->next == NULL)
  {
   *pplist = NULL;
   free(cur);
  }
  else
  {
   pList del = *pplist;
   while (cur->next != NULL)
   {
    del = cur;
    cur = cur->next;
   }
   del->next = NULL;
   free(cur);
  }

 }
}

(9)创造两个指针分别指向前一个结点和后一个节点,分别往后运行,知道前面的指针找到最后一个节点为止,此时后面的指针指向了倒数第二个节点,此时只需要把倒数第二个节点denext指针制成空指针就好了,最后不要忘记释放最后一个节点的空间。

void Display(pList pplist)//打印链表
{
 pList cur = pplist;
 while (cur != NULL)
 {
  printf("%d --> ", cur->data);
  cur = cur->next;
 }
 printf("over\n");
}

(10)只需要创建一个指针然后从前往后遍历就好,依次打印出每个节点的数据就好。

pList Find(pList pplist, Datatype d)//查询相应数据
{
 if (pplist == NULL)
 {
  return NULL;
 }
 pList cur = pplist;
 while (cur != NULL)
 {
  if (cur->data == d)
  {
   return cur;
  }
  cur = cur->next;
 }
 return cur;
}

(11)创造一个指针从前往后遍历每次与需要查找的数据进行对比,是否相同,若相同则返回相应的地址,若不相同则返回空指针。

void Remove(pList *pplist, Datatype d)//删除
{
 assert(pplist);
 if (*pplist == NULL)
 {
  return;
 }
 else
 {
  pList cur = *pplist;
  while (Find(*pplist, d) != NULL && cur != NULL)
  {
   if (cur->data == d)
   {
    pList tmp = cur->next;
    cur->data = cur->next->data;
    cur->next = cur->next->next;
    free(tmp);
    return;
   }
   cur = cur->next;
  }
 }
}

(12)应用查找函数判断是否有要删除的数据,如果有就删除相应的节点,有两种方法:

第一种:把该节点的下一个节点的内容赋给该节点,然后删除下一个节点,

第二种:跟删除最后面的节点有点相似,也是用两个指针从头往后遍历,一前一后,然后找到该节点把该节点的上一个节点的next指针指向该节点的下一个节点。再释放掉该节点就好。
void RemoveAll(pList *pplist, Datatype d)//删除所有
{
 assert(pplist);
 if (*pplist == NULL)
 {
  return;
 }
 else
 {
  pList cur = *pplist;
  while (Find(*pplist, d) != NULL && cur != NULL)
  {
   if (cur->data == d)
   {
    pList tmp = cur->next;
    cur->data = cur->next->data;
    cur->next = cur->next->next;
    free(tmp);
   }
   cur = cur->next;
  }
 }
}

(13)删除所有就是删除掉一个数据后不停止,继续重新查找。直到查找不出来后停止。

void Insert(pList *pplist, int pos, Datatype d)//指定节点插入
{
 assert(pplist);
 //空链表
 if (*pplist == NULL)
 {
  pList tmp = linshi(d);
  *pplist = tmp;
  return;
 }
 //非空链表
 else
 {
  pList cur = *pplist;
  pos = pos - 1;
  pList tmp = linshi(d);
  while (pos--&&cur->next != NULL)
  {
   cur = cur->next;
  }
  if (pos > 0)
  {
   cur->next = tmp;
   tmp->data = d;
   tmp->next = NULL;
   return;
  }
  tmp->data = cur->data;
  cur->data = d;
  tmp->next = cur->next;
  cur->next = tmp;
 }
}

(14)指定节点插入跟我下一篇博客要说的无头非尾链表插入有着相似的原理,这个也有两种方法求解:

第一种:用两个指针一前一后往后遍历,直到前面的指针找到要插入的节点,然后创造一个新的节点暂时命名为tmp,让tmp节点的next指针指向前面的指针所指向的节点,然后再让后面zhizhendenext指针指向tmp节点,最后再对tmp的data进行赋值就完成节点插入了。

第二种:定义一个指针让其指向需要插入的节点,然后创造一个新的节点暂时命名为tmp,先让tmp节点的next指针指向该节点的下一个节点,然后让该节点的next指针指向tmp节点,成功添加了一个节点后就是进行值交换,创造一个临时变量temp进行值交换就好。
void Insert_Remove(pList *pplist, int pos)//指定节点删除
{
 assert(pplist);
 //空链表
 if (*pplist == NULL)
 {
  return;
 }
 //非空链表
 else
 {
  pList cur = *pplist;
  pList tmp = *pplist;
  pos = pos - 1;
  while (pos--&&cur->next != NULL)
  {
   cur = cur->next;
  }
  if (pos != 0)
  {
   pList del = *pplist;
   while (tmp->next != NULL)
   {
    del = tmp;
    tmp = tmp->next;
   }
   del->next = NULL;
   free(cur);
   return;
  }
  tmp = cur->next;
  cur->data = cur->next->data;
  cur->next = cur->next->next;
  free(tmp);
 }
}

(15)代码思想:用两个指针一前一后往后遍历,直到前面的指针找到要插入的节点,然后用后面指针所指向节点的next指针指向前一个指针所指向节点的后一个节点。然后free掉前一个指针所指向的空间。

pList Reverse(pList *pplist)//逆序
{
 assert(pplist);
 pList cur = *pplist;
 pList head = cur->next;
 pList newhead = NULL;
 head = cur->next;
 cur->next = NULL;
 newhead = cur;
 while (head != NULL)
 {
  cur = head;
  head = cur->next;
  cur->next = newhead;
  newhead = cur;
 }
 return newhead;
}

(16)节点跟数组不同,不能从后往前走,所以只能把第一个拿出来,然后从第二个开始依次拿出来用头插的方法依次插入到新链表的最开始,最后返回新联标的头地址就好。
void Sort(pList *pplist)//排序  从小到大
{
 assert(pplist);
 pList cur = *pplist;
 int count = 0;
 int i, j;
 while (cur != NULL)
 {
  count++;
  cur = cur->next;
 }
 cur = *pplist;
 for (i = 0; i < count - 1; i++)
 {
  cur = *pplist;
  for (j = 0; j < count - 2 - i; j++)
  {
   if (cur->data > cur->next->data)
   {
    Datatype tmp;
    tmp = cur->data;
    cur->data = cur->next->data;
    cur->next->data = tmp;
   }
   cur = cur->next;
  }
 }

}

(17)跟冒泡排序,或者选择法排序没有什么区别,只需要最开始的时候计算出来一共有多少个节点就好,只不过现在交换的不是整个节点而是该节点的data值。

int main()
{
 pList head;
 InintLinkList(&head);

 /*PushFront(&head, 1);
 PushFront(&head, 2);
 PushFront(&head, 3);
 PushFront(&head, 4);
 PushFront(&head, 5);
 PushFront(&head, 6);
 Display(head);*/
 //PopFront(&head);
 //PopFront(&head);
 //PopFront(&head);
 //Display(head);

 //PushBack(&head, 1);
 //PushBack(&head, 2);
 //PushBack(&head, 7);
 //PushBack(&head, 4);
 //PushBack(&head, 7);
 //PushBack(&head, 6);
 //PushBack(&head, 7);
 //PushBack(&head, 8);
 //PushBack(&head, 9);
 //Display(head);
 //PopBack(&head);
 //PopBack(&head);
 //PopBack(&head);

 //pList ret = Find(head, 11);
 //if (ret == NULL)
 //{
 // printf("没找到!\n");
 //}
 //else
 //{
 // printf("找到了!\n");
 //}
 //Remove(&head, 7);
 //RemoveAll(&head, 7);
 //Insert(&head, 2, 11);
 //Insert_Remove(&head, 13);
 /*pList tmp = Reverse(&head);
 Display(tmp);*/
 //Sort(&head);
 Display(head);

 system("pause");
 return 0;
}

以上就是我对该代码一点解释,若有看不懂的地方可在下方的评论处写下不懂的地方,我会及时作出回复的,或者你认为有什么地方可以改进,也希望可以提出宝贵的建议。
谢谢!


原创粉丝点击