链表应用:两数相加

关于链表

链表是一种极其重要的数据结构,因为对指针和抽象思维的要求较高,一度成为身边同学最痛恨的对象。

我在将这里演示如何使用链表制作一个可以按位储存数字的容器。鉴于本人亦初学者,有错误请各位在评论区指正。

这里还是以介绍链表为主,算法部分苦于我表达力不足,只能尽量多写注释,望海涵。

链结

链结是链表最基本的结构。

//链结的结构其实非常简单
//typedef 的声明方式可以避免之后重复写struct。
//C++选手可以无视这种方法。
typedef struct _Node
{
	//Data field
	int num;
	
	//Pointer, points to the next node.
	_Node* next;
}Node;

num用来存储一位数字,每一个Node代表一位数。

严格按照如下概念图来构造链表:

Head
A
B
许多节点
NULL

从空链表开始

链表的头和尾是确定的(Head和NULL)。

//如果忽略head存储的值,这就是一个简单的空链表。
Node* Head={val,NULL};
//不过更常见的是下面这种方式:
//我们需要的不过是一个可以指向链结的东西
Node* Head=NULL;

我们要做的就是实现一个可以向里面添加链结的函数,来扩充我们的链表。

添加链结

添加链结最主要的方法有3:头插法、尾插法、中插法。中插法更适合因地制宜地去使用,这里主要演示前两种。
·

头插法

//头插法
//返回的是指向新加入的链结的指针
Node* add(Node** phead,int val)//要改变指针本身,就传入指针的地址。
{
	//构建一个待插入的链结
	Node* node=(Node*)malloc(sizeof(Node));
	/*一定要使用malloc把node存储在堆区(heap),
	  直接在函数内声明的变量会在函数调用结束的时候灰飞烟灭
	  这样返回的指针指向的地址没有意义。*/
	node->num=val;
	node->next=NULL;//养成好习惯
	
	//把链结node加入链表中去
	Node* temp=*phead;
	*phead=node;
	node->next=temp;

	return node;
}

·
如果暂时感觉到难以理解,不妨拿起纸笔画一下上面的概念图,理清思路,体会一下C语言的招牌特色:面向过程。
`

//由add可以实现一个链表构造函数
Node* struct_list(int num)//输入需要的节点数量
{
	Node* head = NULL;
	
	for(int ix = 0; ix < num; ix++)
		add(&head, val);//val值随意
	
	return head;
}

`

尾插法

尾插法比头插法复杂一些,需要用一个静态变量static Node* tail来记录链表尾的位置。static变量存在于静态区,在整个文件中有效,不用担心会被刷新。
因为其文件作用域特性,static有时候也会被用来声明一个只在这个源代码文件中有效的变量。而不加static会变为全局变量(global variable)。

//尾插法
Node* add(Node** phead,int val)
	//构建一个待插入的链结
	Node* node=(Node*)malloc(sizeof(Node));
	
	node->val=val;
	node->next=NULL;
	
	//把链结node加入链表中去
	static Node* tail=NULL;//避免野指针
	
	if(!phead)//在这里,需要判断链表是否非空
	{
		*phead=node;
		tail=node;
	}
	else
	{
		tail->next=node;
		tail=tail->next;
	}

	return node;
}

也可以基于这个原理写一个构造链表函数。你依然可以根据自己的想法去实现更有特色的构造链表函数。

其实要实现主题功能,学习到这里就已经够了。不过我这里还是会介绍一下链表其他的操作以及其对应的函数。

哦,别忘了删除链表,及时释放内存:

void release_list(Node** phead)
{
	Node* tail,*ptr;
	
	if(!*phead)//空链表,无需释放,退出
        return;

	for(tail=*phead,ptr=tail->next; ptr; tail=ptr,ptr=ptr->next)
		free(tail);
	//链表的for遍历
	free(tail);
	*phead=NULL;
}

我们注意到在如上代码里面有两种常见的代码写法:

Node* ix;
for(ix = head; ix; ix = ix->next)

用指针迭代链表是非常重要的操作,ix=ix->next是指针移动的方式。
条件 ix 可用是因为NULL有如下宏定义:

#define NULL 0
//限定Visual Studio 2019
//C++是nullptr
//相应的,一般对指针操作时要保证指针非空,可以采取如下形式:
if(ptr&&(Condition))
...

掌握了如上原理,以下的操作可以自己实现

遍历打印链表内容

void print_list(const Node* head)//不希望改变值,使用const
{
    for (; head ;head = head->next)
        printf("%-4d", head->num);//%-4d表示用4格空间,左对齐打印一个int。
}

删除链结

可能有一点难度,不妨对照概念图多试几次

void delete_node(Node** phead,int key)
{
    Node *tail = *phead, *ptr = NULL;

    if(!tail)//空链表,异常
        return;

    ptr = tail->next;

    while(ptr)//从第二个开始搜索目标,如果只有一个链结,循环就不会执行
    {
        if(ptr->num==key)
        {
            tail->next = ptr->next;
            free(ptr);
            ptr = tail->next;
        }
        else
        {
            tail = ptr, ptr = ptr->next;
        }
        
    }
    if((*phead)->num==key)//检查第一个是不是目标
    {
        *phead = ptr;
        free(*phead);

        if(ptr)
        {
            tail = ptr;
            ptr = ptr->next;
        }
    }
}

*:搜索链结不妨自己试一下。

两数相加

这是leetcode里面的一道题,同前面两数之和难度形成对比。。
原题:

对于用链表倒序表示的两个数,要求实现两数相加
考虑情况:

1,进位
2,链表不等长(相加的数的位数不一)
3,和的位数高于两链表位数,需要添加。
原题

//我们可以在刚才的基础下,构建这样一个算法。
//假定两个链表l1,l2已经给出。
Node* addNumbers(Node* l1, Node* l2)
{
    Node *head = NULL;//新链表
    Node *tail = NULL;

    int carry = 0;//进位值
    int n1, n2;//两数
    int sum = 0;//两数相加

    while (l1||l2)//解决问题2,取l1,l2最长长度
    {
        n1 = l1 ? l1->num : 0;
        n2 = l2 ? l2->num : 0;//不够长的部分视作0

        sum = n1 + n2 + carry;
        carry = sum / 10;//利用窄化,抛弃小数部分

        if(!head)//第一次执行,tail未曾初始化
        {
            head = (Node *)malloc(sizeof(Node));
            *head = Node{sum % 10, NULL};//结构体字面量,也叫做无名结构体
            tail = head;
        }

        else//tail 已经初始化
        {
            tail->next=(Node*)malloc(sizeof(Node));
            tail=tail->next;
            tail->num=sum%10;
            tail->next=NULL;
        }

        if(l1)//向后推
            l1 = l1->next;
        if(l2)
            l2 = l2->next;
    }

    if(carry>0)//注意最后一位可能还需要进位
    {
        tail->next = (Node *)malloc(sizeof(Node));
        *tail->next = Node{carry, NULL};
    }

    return head;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值