算法描述:
在《啊哈算法》上有介绍模拟链表的实现。链表这种数据结构可以通过数组来实现,我们需要两个数组,其中一个数组data存放数据,即数据域。另外一个数组right用来存放每一个数的右边的数的位置,即指针域。
在上图中,列如data[3]的下一个元素是data[4],下标为4 ,所以right[3]的值为4。又因为data[9]后没有元素,所以right[9]的值用0表示。
如果我们要在data[4]数字8之前插入数字6,只需将6放到data数组末尾data[10],接下来将right[3]指向10,将right[10]指向4。
算法实现(上):
代码参考了《啊哈算法》上的实现,但实际上书上的代码是有问题的。
/*核心思路:
查找是否存在一个节点的下一个节点的数据大于待插入的节点,即data[right[t]]>data[len]
如果找到了,就修改right指针域。最后根据right指针域打印其数据域的值
*/
#include <stdio.h>
int data[101];
int right[101];//每个节点都有一个指针域,所以这里的数组长度要和data数组一样长
int main(){
/*n为用户首次录入的节点总数,用于控制初始化节点时的循环次数
len为当前节点总数,为方便操作,
data[0]和right[0]不放内容,从下标1开始遍历,因此data[len]就是最后一个元素
*/
int n,i,len,t;
scanf("%d",&n);
//初始化数组data
for(i = 1;i<=n;i++){
scanf("%d",&data[i]);
}
len = n;
//初始化数组right
for(i = 1;i<=n;i++){
if(i!=n){
right[i] = i+1;
}else{
right[i] = 0;//用0表示没有下一个元素
}
}
//在data的末尾添加一个数
len++;
scanf("%d",&data[len]);
/*
通过遍历指针域,当找到某个节点的数据值大于插入的节点data[len]时,调整指针域的指向。
直到指针域遍历完成为止
*/
t = 1;
while(t!=0){
if(data[right[t]]>data[len]){
right[len] = right[t];
right[t] = len;
break;
}
t = right[t];
}
t = 1;
while(t!=0){
printf("%d ",data[t]);
t = right[t];
}
//等待用户键盘录入,以起到暂停程序目的
getchar();
getchar();
return 0;
}
程序验证:
第一组:
3
1 3 5
4
输出:1 3 4 5
第二组:
3
1 3 5
7
输出:1 3 5
第三组:
1
3
1
输出:3
测试发现,当用户要插入的数据小于data[1]或者大于最后一个数时,结果输出有误。
结合下面的代码段和这张图可知,当待插入数小于第一个数时,程序是将data[2]和插入数据进行比较;若是待插入的数大于最后一个数,当程序执行到right[t]==0时,由于data[0]未定义,程序也将不会进入if语句段修改其指针域。
t = 1;
while(t!=0){
if(data[right[t]]>data[len])
算法修正
既然当待插入数大于最后一个数时,没有进入if(data[right[t]]>data[len])语句块,那么只需做一个标记,当未进入该语句块后,就修改最后一个数和倒数第二个数的指针域。
当插入数小于第一个数时,比如对于下面这种情况,由于right数组表示的是指向下一个节点的值,所以直接更改right[1]的值并不正确。
后来想到了可以添加一个头节点,即添加一个right[0],这样当插入数小于第一个数时,只需修改right[0]和right[len]两个指针域即可。
算法实现(下):
/*
核心思路:查找是否存在一个节点的下一个节点的数据大于待插入的节点,即data[right[t]]>data[len]
如果找到了,就修改right指针域。最后根据right指针域打印其数据域的值
*/
#include <stdio.h>
int data[101];
int right[101];//每个节点都有一个指针域,所以这里的数组长度要和data数组一样长
int main(){
/*n为用户首次录入的节点总数,用于控制初始化节点时的循环次数
len为当前节点总数,为方便操作,data[0]和right[0]不放内容,从下标1开始遍历,因此data[len]就是最后一个元素
*/
int n,i,len,t;
int flag = 1;
scanf("%d",&n);
//初始化数组data
for(i = 1;i<=n;i++){
scanf("%d",&data[i]);
}
len = n;
//初始化数组right
for(i = 0;i<=n;i++){
if(i!=n){
right[i] = i+1;
}else{
right[i] = 0;//用0表示没有下一个元素
}
}
//在data的末尾添加一个数
len++;
scanf("%d",&data[len]);
/*
通过遍历指针域,当找到某个节点的数据值大于插入的节点data[len]时,调整指针域的指向。
直到指针域遍历完成为止
*/
t = 1;
//对插入节点小于第一个节点的情况做特殊处理。(这里放在if..else里是出于无需再走循环的目的)
if(data[len]<data[1]){
right[0] = len;
right[len] = 1;
}else{
while(t!=0){
if(data[right[t]]>data[len]){
flag = 0;
right[len] = right[t];
right[t] = len;
break;
}
t = right[t];
}
//插入节点大于最后一个节点的情况
if(flag == 1){
right[len] = 0;
/*这里只做简单处理,也可以查找right[t]==0的节点修改其指针域,
只需在上面循环中加入if(right[t]==0){temp = t;}
这里再改成right[temp] = len;
*/
right[len-1] = len;
}
}
//t为right[0]头节点指向的首节点的下标
t = right[0];
while(t!=0){
printf("%d ",data[t]);
t = right[t];
}
//等待用户键盘录入,以起到暂停程序目的
getchar();
getchar();
return 0;
}
程序验证:
第一组:
3
1 3 5
4
输出:1 3 4 5
第二组:
3
1 3 5
7
输出:1 3 5 7
第三组:
1
3
1
输出:1 3