修正《啊哈算法》上模拟链表算法的错误

本文探讨了使用数组实现链表的数据结构,并详细讲解了一种插入算法的实现及其修正过程,确保了数据的正确排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法描述:

  在《啊哈算法》上有介绍模拟链表的实现。链表这种数据结构可以通过数组来实现,我们需要两个数组,其中一个数组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 
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值