首先请尽量理解下面所给出的一个例子,它是我所给出的例子中的 有序的单链表最终版:
//-----------------------【有序单链表最终版】------------------
// [3/10/2017 熊]
//----------------------------------------------------------
//-----------------------【头文件部分】-----------------------
#include<stdio.h>
//-----------------------【宏定义部分】-----------------------
#define TRUE true
#define FALSE false
//-----------------------【结构体声明】-----------------------
struct List
{
int rank;
List* next;
};
//-----------------------【GetList()函数】-------------------
//描述:插入新节点的函数
//----------------------------------------------------------
bool InsertIndex(List** rplink, int new_rank)
{
List* current; //声明一个指向当前节点的指针
List* new_index; //声明一个新的节点
//使用循环来判断是否达到需要插入的节点,即通过 new_value < *rplink -> rank来判断是否插入点
while ((current = *rplink) == NULL /* 相当于 current = current->next */ || new_rank < current->rank)
{
rplink = ¤t->next; //让根指针指向前一个节点内的指针,即指向以一个节点的结构体内的指针
}
new_index = new List;
if (new_index == NULL)
{
return FALSE; //动态分配获取内存失败 返回 FALSE
}
new_index->rank = new_rank; //赋值操作
//new节点插入
new_index->next = current;
*rplink = new_index; //请记住rplink是指向节点内的指针的指针 因此不需要(*rplink) -> next = new_index;
return TRUE;
}
各位客官如果不能够理解的话就请接着往下看吧:-D,我会给出一个对比版请给位客官在仔细看完后对比终极版和对比版之间的区别点与共同点
对于有序单链表的创建,我们首先会需要一个根指针来记住起始位置:
//-----------------------【结构体声明】-----------------------
struct List
{
int rank;
List* next;
};
//首先我们会定义一个根指针来作为有序链表的头
//【全局变量部分】
List head = { 0 };
然后就是插入函数的实现:
bool InsertIndex(int rank)
{
List* current = head.next;
List* previous = &head; //用于保存当前节点之前节点的指针变量
List* new_index;
while (current != NULL & head.rank < rank)
{
previous = current;
current = current->next;
}
new_index = new List;
if (new_index == NULL)
{
return FALSE;
}
new_index->rank = rank;
new_index->next = current;
previous->next = new_index;
return TRUE;
}
现在让我们仔细分析一下上面的例子:
1、 //首先我们会定义一个根指针来作为有序链表的头
//【全局变量部分】
List head = { 0 };
我想一定会有人问为什么不想终极版使用root指针作为记录其实位置的工具呢?(终极版的rplink就是根指针)
各位客官可以亲自私下试一试如果在这里不用全局变量head来记录开始位置,在while (current == NULL || (current->rank) > rank)
这里程序就会崩溃!然后就会发现((current->rank) > rank)这步使用野指针。这是因为链表刚开始建立,触头链表并无其他节点,所以在循环一次过后在此进入判断时
然后程序就该崩溃,因为我们无法获取current->rank的值。
最后给客官们展示下整体代码:
//-----------------------【有序单链表对比版】-----------------
//描述:与终极版的对比版
//------------------------------------------------------------
#include <stdio.h>
//-----------------------【宏定义部分】-----------------------
#define TRUE true
#define FALSE false
//-----------------------【结构体声明】-----------------------
struct List
{
int rank;
List* next;
};
//首先我们会定义一个根指针来作为有序链表的头
//【全局变量部分】
List head = { 0 };
//【InsertIndex()函数部分】
bool InsertIndex(int rank)
{
List* current = head.next;
List* previous = &head; //用于保存当前节点之前节点的指针变量
List* new_index;
while (current != NULL & head.rank < rank)
{
previous = current;
current = current->next;
}
new_index = new List;
if (new_index == NULL)
{
return FALSE;
}
new_index->rank = rank;
new_index->next = current;
previous->next = new_index;
return TRUE;
}
现在客官们可以思考一下终极版和对比版之间的共同点和不同点!!
首先,客官们一定会说刚刚所提到的不同点,终极版用的是rplink指针来记录起始位置,而对比版使用全局变量。但是客官们知道为什么对比版之所以是对比版吗?因为把root指针设置为全局变量head时,插入函数能够修改它,但是这是最坏的一种解决方案。因为这样一来,函数就只对这个链表起作用。
共同点客官们能找出来吗?
两者其实都是需要对插入节点之前的节点进行修改。如果不懂下面我在C和指针中找到咯一个很容易被大家理解的图片:
这也是我之所以用rplink指针作为记录链表起始位置的工具的原因,因为我们可以直接让根指针指向节点中的指针就像终极版一样简单明了,和对比版相比较少咯很多冗杂的赋值操作而且我在while循环还用咯一个窍门,客官们都可以仔细看看,它嵌入了对current的赋值while ((current = *rplink) == NULL || new_rank < current->rank)
最后我在C和指针中看到咯大佬的一句话很给力!
:消除特殊情况是这个函数更为简单,这个改进之所以可以行是由于两方面的因素。第一个因素是我们正确解释问题的能力。除非你可以在看上去不同的操作中总结出共性,不然你只能编写额外的代码来处理特殊情况。
——《C和指针》Kenneth A.Reek