C语言 超详细介绍与实现线性表中的无头单向非循环链表

 更新时间:2022年03月29日 16:42:16   作者:李逢溪  
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多

一、本章重点

  • 无头单向非循环链表介绍
  • 无头单向非循环链表常用接口实现
  • 在线oj训练

二、链表介绍

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的。

简单的说:链表就是一些结构体相互关联起来,而关联的手段就是指针,通过存储下一个结构体的地址,就能挨个访问存在结构体里的数据。

相比于顺序表来说。链表能够解决顺序表的一些缺点。

  • 从内存角度来说:无论是静态顺序表还是动态顺序表都会有一定的内存浪费,链表则是用一个节点申请一个节点,无内存浪费。
  • 从效率角度上来说:顺序表不管是头插还是中间插入与删除都要移动数据,时间复杂度是O(N)。而链表则只要改变指针指向即可达到插入和删除的效果。

实际中要实现的链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向、双向

2. 带头、不带头

3. 循环、非循环

带头:

存在一个哨兵位的节点,该节点不存储任何有效数据,属于无效节点,但通过这个无效节点当头节点让我们在某些方面使用会有一些优势。

比如,你头插新节点时,不需要传二级指针,因为我们的头节点始终指向这个无效节点。

带头单向非循环链表

双向:

指的是节点中不再只有一个指针,而是有两个指针,一个指向前一个节点,另一个指向后一个节点。

无头双向非循环链表

循环:

尾指针不再指向NULL,而是指向头节点。 

无头单向循环链表

三、无头单向非循环链表常用接口实现

3.1动态申请一个节点

SLTNode* BuySListNode(SLDataType x)
{
	SLTNode* newnode = malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

3.2单链表打印

void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

3.3单链表尾插

void SListPushBack(SLTNode** pphead, SLDataType x)
{
	if (*pphead==NULL)
	{
		*pphead = BuySListNode(x);
		return;
	}
	else
	{
		SLTNode* tail;
		tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = BuySListNode(x);
		return;
	}
}

3.4单链表的头插

void SListPushFront(SLTNode** pphead, SLDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

3.5单链表的尾删

void SListPopBack(SLTNode** pphead)
{
	if (*pphead == NULL)
	{
		return;
	}
	else if((*pphead)->next==NULL)
	{
		*pphead = NULL;
	}
	else
	{
		SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

3.6单链表头删

void SListPopFront(SLTNode** pphead)
{
	if (*pphead == NULL)
	{
		return;
	}
	else
	{
		SLTNode* temp = *pphead;
		(*pphead) = (*pphead)->next;
		free(temp);
	}
}

3.7单链表查找

SLTNode* SListSearch(SLTNode* phead, SLDataType x)
{
	while (phead)
	{
		if (phead->data == x)
		{
			return phead;
		}
		phead = phead->next;
	}
	return NULL;
}

3.8单链表在pos位置之前插入x

void SListInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	if (*pphead == NULL)
	{
		return;
	}
	else if(*pphead==pos)
	{
		newnode->next = pos;
		*pphead = newnode;
	}
	else
	{
		SLTNode* cur = *pphead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		newnode->next = pos;
		cur->next = newnode;
	}
}

3.9单链表删除pos位置的节点

void SListErase(SLTNode** phead, SLTNode* pos)
{
	if (*phead == NULL)
	{
		return;
	}
	else if (*phead == pos)
	{
		*phead == NULL;
	}
	else
	{
		SLTNode* cur = *phead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
	}
}

四、在线oj训练

4.1移除链表元素(力扣)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

输入:head = [1,2,6,3,4,5,6], val = 6

输出:[1,2,3,4,5]

输入:head = [7,7,7,7], val = 7

输出:[]

 思路:

前后指针(跟班指针),初始转态prev指向NULL,cur指向head,如果出现cur->val==val.

就进行删除,删除步骤为prev->next==cur->next;cur=cur->next。迭代过程为:prev=cur,cur==cur->next。

特殊情况,如果要删除的val为头节点,则删除步骤为head=cur->next;free(cur);cur=head。

代码参考:

typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{
    ListNode* cur=head;
    ListNode* prev=NULL;
    while(cur)
    {
        if(cur->val==val)
        {
            if(cur==head)
            {
                head=cur->next;
                free(cur);
                cur=head;
            }
            else
            {
                 prev->next=cur->next;
                 cur=cur->next;
            }
        }
        else
        {
            prev=cur;
            cur=cur->next;
        }
    }
    return head;
}

4.2反转单链表(力扣)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

输入:head = []

输出:[]

这里提供一个头插思路:newhead=NULL,将head中的数据从左到右依次头插至newhead链表中。

typedef struct ListNode ListNode; 
struct ListNode* reverseList(struct ListNode* head)
{
    ListNode* newhead=NULL;
    ListNode* cur=head;
    if(cur==NULL)
    {
        return cur;
    }
    ListNode* next=cur->next;
    while(cur)
    {
        cur->next=newhead;
        newhead=cur;
        cur=next;
        if(next)
        next=next->next;
    }
    return newhead;
}

注意:结束条件是cur==NULL;此时如果next往后走,next=next->next;会出现NULL解引用的问题。

其次要考虑cur==NULL的问题,不然ListNode* next=cur->next也是对空指针解引用。

其实可以化简为:

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    while(cur)
    {
       struct ListNode* next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next; 
    }
    return newhead;
}

到此这篇关于C语言 超详细介绍与实现线性表中的无头单向非循环链表的文章就介绍到这了,更多相关C语言 无头单向非循环链表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言memset函数详解

    C语言memset函数详解

    这篇文章主要介绍了C语言中的memset()函数,包括其与memcpy()函数的区别,需要的朋友可以参考下,希望能够给你带来帮助
    2021-09-09
  • 基于C++实现去除字符串头尾指定字符功能

    基于C++实现去除字符串头尾指定字符功能

    编程时我们经常需要对字符串进行操作,其中有一项操作就是去除字符串的头(尾)指定的字符,比如空格。本文为大家详细介绍了如何利用C++实现这一效果,需要的可以参考一下
    2022-04-04
  • 二叉树前序遍历的非递归算法

    二叉树前序遍历的非递归算法

    这篇文章主要介绍了二叉树前序遍历的非递归算法,需要的朋友可以参考下
    2014-02-02
  • 用c++实现x的y次幂的代码

    用c++实现x的y次幂的代码

    以下实例是对使用c++实现x的y次幂的解决方法进行了介绍。需要的朋友参考下
    2013-05-05
  • C语言修炼之路数据类型悟正法 解析存储定风魔上篇

    C语言修炼之路数据类型悟正法 解析存储定风魔上篇

    使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。您可能需要存储各种数据类型的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么
    2022-02-02
  • 解析C++中的5个存储类的作用

    解析C++中的5个存储类的作用

    这篇文章主要介绍了C++中的5个存储类的作用,存储类是管理对象的生存期、链接和内存位置的类型说明符,需要的朋友可以参考下
    2016-05-05
  • 浅析VSCode tasks.json中的各种替换变量的意思 ${workspaceFolder} ${file} ${fileBasename} ${fileDirname}等

    浅析VSCode tasks.json中的各种替换变量的意思 ${workspaceFolder} ${file} ${

    这篇文章主要介绍了关于VSCode tasks.json中的各种替换变量的意思 ${workspaceFolder} ${file} ${fileBasename} ${fileDirname}等,本文给大家介绍的非常详细,对大家的学习或工作具有参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • C经典算法之二分查找法

    C经典算法之二分查找法

    这篇文章主要介绍了C经典算法之二分查找法的相关资料,这里提供两种方法帮助大家实现这样的功能,需要的朋友可以参考下
    2017-10-10
  • Qt6远程连接MySQL数据库的简单易上手版

    Qt6远程连接MySQL数据库的简单易上手版

    在Qt应用程序里,可实现远程MySQL服务器的连接操作,本文就来介绍一下Qt6远程连接MySQL数据库,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • MFC程序中使用QT开发界面的实现步骤

    MFC程序中使用QT开发界面的实现步骤

    本文主要介绍了MFC程序中使用QT开发界面的实现步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12

最新评论