数据结构 | 单链表实现

————————————————————————————————————————————

单链表

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

概念://单链表基本概念图(基本中的基本)

定义:

//初始化

带头结点

1 void InitList(Node **head)
2 {
3     *head = (Node *)malloc( sizeof(Node));
4     (*head)->next = NULL;
5 }

不带头结点

1 void InitList(Node **head)
2 {
3     *head = NULL;
4 }

//插入:

带头结点

 1 void CreatList(Node **head)
 2 {
 3     Node *r = *head, *s;
 4     int a;
 5     while(scanf("%d", &a))
 6     {
 7         if(a != 0)
 8         {
 9             s = (Node *)malloc(sizeof(Node));
10             s->value = a;
11             r->next = s;
12             r = s;
13         }
14         else
15         {
16             r->next = NULL;
17             break;
18         }
19     }
20 }

不带头结点

 1 void CreatList(Node  **head)
 2 {
 3     Node *p, *t;        /*p工作指针,t临时指针*/
 4     int a, i = 1;
 5     while(scanf("%d", &a))
 6     {
 7         if(a != 0)
 8         {
 9             t = (Node *)malloc(sizeof(Node));
10             t->value = a;
11             if(i == 1)
12             {
13                 *head = t;
14             }
15             else
16             {
17                 p->next = t;
18             }
19             p = t;
20         }
21         else
22         {
23             p->next = NULL;
24             break;
25         }
26         i++;
27     }
28 }

//操作

带头结点

1 Node *temp;
2 temp = head->next;
3 while(temp != NULL)
4 {
5     //...
6     temp = temp->next;
7 }

不带头结点

1 Node *temp;
2 temp = head;
3 while(temp != NULL)
4 {
5     //...
6     temp = temp->next;
7 }

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

如何在单链表中插入节点:

//头插法

在堆中申请一个新的内存空间用来存放结点,填充信息域,把Head指针的指向擦除并指向新的结点,新的结点指向原来的第一个结点的地址;

示例:

输入:0 1 2 3 4 5 6 7 8 9

输出:

分析:

//第一次插入节点

//循环i执行第二次插入

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

//尾插法

将数据插在链表的尾部,修改最后节点的指针指向新节点,新节点的指针指向NULL

示例:

输入:0 1 2 3 4 5 6 7 8 9

输出:

分析:

//第一次插入节点

//循环i执行第二次插入

示例2

分析://与上一例同理

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

链表传参的三种方式

//实参传head的地址,形参定义双指针

在该种方式中关于为什么要在AddNode方法前加 **,分析在本文末尾

//实参传指针head,形参取地址

//返回值调用

利用函数的返回值,返回链表头。

缺点是不能返回多个函数。

示例:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

释放链表空间

 1 void ReleaseHead(struct Test **head) //释放链表空间
 2 {
 3     struct Test *temp;
 4     while(*head != NULL)
 5     {
 6         temp = *head;
 7         *head = (*head) ->next;
 8         free(temp);
 9     }
10 }

示例:

输出:

//输出执行成功,此时head==NULL;

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

关于为什么要使用双指针调用(新增/释放节点时)

示例:

输出:

//使用双指针即正确输出

//使用注释掉的语句(单指针)则head指针的地址为NULL,此时head没有申请分配到地址

结论:

当调用AddNode传递的是&head,接收的形参是**head时,链表正常。

当传递的是head,接收的形参是*head时,指针head在AddNode中没有被修改,依然处于未分配内存空间的状态。

分析:

在这里和正常函数值传递是一样的,假设int a,作为参数传入时没有被修改,修改的是拷贝的临时变量a,所以需要a的指针。在这里同理,也是需要传入head的指针,即指向Data结构体的指针的指针。

如果主函数调用的是AddNode(head);,函数调用时取head的地址 void AddNode(struct Data *&head),结果也是有效的

一般来说, 初始化链表之类的需要双指针(例如:翻转链表因为不是在原始链表上翻转,而是返回新的链表头,所以也需要双指针)。像求链表长度、插入节点、打印输出就只要单指针。

在子函数中传递指针时,子函数的形参要用指针的地址,就是双重指针,也叫二级指针。定义二重指针目的是为了当指针作为函数参数时候,在链表调用时候能改变头指针的地址,从而能调动整个链表,用一级指针的话只能改变一个结点的值。

posted @ 2017-04-22 15:51  hugh.dong  阅读(419)  评论(0编辑  收藏  举报