介绍
顺序表和链表都是数据结构中常见的线性表。它们的主要区别在于内存管理方式不同。
顺序表(Array)是由一系列元素按照一定顺序依次排列而成,它使用连续的内存空间存储数据。顺序表使用一个数组来存储数据,数组中的每个元素都可以通过下标来访问。顺序表具有随机访问、空间利用率高等优点,但在插入和删除元素时需要移动其他元素,导致效率低下。在需要频繁修改元素的情况下,顺序表不适合使用。
链表(List)是一种通过指针来实现的动态数据结构,可以无序存储任意类型的数据。链表中的元素被称为节点,每个节点中都包含了数据和指向下一个节点的指针。相比之下,链表的内存分配是动态的,并且不需要提前规划数组大小,能够有效地避免内存浪费。链表可以快速地插入和删除节点,但是由于插入和删除必须先定位到指定位置,因此其访问时间比顺序表长。
总之,当需要频繁查询顺序、频繁访问内部元素和使用预定义的固定大小存储数据时,使用顺序表可能是比较合适的选择。但当需要大量插入、删除和更改元素和无法提前预测空间大小时,链表的动态分配方式更为有效。
实现顺序表反转
/***************************************
* 函数功能:逆置顺序表
* 函数参数:int*data-表示待逆置的顺序表 int size-表示顺序表的大小
* 函数返回值:无
****************************************/
void reverse(int*data, int size)
{
for (int i = 0; i < size / 2; ++i)
{
int temp = data[i];
data[i] = data[size - i - 1];
data[size - i - 1] = temp;
}
}
在函数体中,发生了一次 for 循环,该循环的次数与数据规模 N 成线性关系。另外,for 循环中进行了常数次的赋值操作。因此,整个函数的时间复杂度是 O(N)。(其中N表示顺序表的长度)
该函数的空间复杂度为 O(1)(常数级别)。在函数执行期间,只使用了恒定数量的变量(i、temp),而且使用的空间不随问题规模 N 的增加而增加,因此该函数的空间复杂度为 O(1)。
因此,该函数的时间复杂度为 O(N),空间复杂度为 O(1)。
实现链表反转
核心代码
/***************************************
* 函数功能:逆置链表
* 函数参数:struct myHead* list待逆置链表
* 函数返回值:无
****************************************/
void reverseList(struct myHead* list)
{
struct myNode*p = list->element->next,*q = NULL;
// 遍历链表
while (p)
{
// 保存p的后继节点
struct myNode*temp = p->next;
// 改变指针指向的位置
p->next = q;
q = p;
p = temp;
}
// 保存反转链表的结果
list->element->next = q;
}
算法的核心设计思想:改变链表中节点指针指向的位置,也就是改变前驱与后继元素的位置。
若链表中的元素为1、2、3、4、5那么反转的过程图如下:
该函数主要有两个操作:遍历链表和反转链表。在遍历链表过程中,需要访问、操作每个节点以及节点中存储的数据。因此,函数的时间复杂度为 O(N)。(其中 N 表示链表长度)
函数的空间复杂度为 O(1)。函数只使用了常数级别的额外空间来存储一些临时变量,而且这些变量的空间占用与问题规模 N 无关,因此可以认为函数的空间复杂度为 O(1)。
附链表的一些中间函数
链表的结构文章来源:https://www.toymoban.com/news/detail-472271.html
struct myNode
{
int data;
struct myNode*next;
};
struct myHead
{
int size;
struct myNode*element;
};
链表的一些基本操作,包括初始化、添加节点与输出链表。文章来源地址https://www.toymoban.com/news/detail-472271.html
/***************************************
* 函数功能:链表初始化
* 函数参数:无
* 函数返回值:无
****************************************/
struct myHead*listInit(void)
{
// 初始化头结点
struct myHead*myList = (struct myHead*)malloc(sizeof(struct myHead));
myList->size = 0;
// 添加虚拟节点
myList->element = (struct myNode*)malloc(sizeof(struct myNode));
myList->element->data = -1;
myList->element->next = NULL;
return myList;
}
/***************************************
* 函数功能:使用尾插法插入值至链表中
* 函数参数:struct myHead*list表示目标链表 int data表示待插入的值
* 函数返回值:无
****************************************/
void addNode(struct myHead*list, int data)
{
struct myNode*p = list->element;
// 遍历至待插入节点的前驱节点
while (p->next)
p = p->next;
// 新建节点
struct myNode*myNode = (struct myNode*)malloc(sizeof(struct myNode));
myNode->data = data;
myNode->next = NULL;
// 插入节点并增加链表节点数量
p->next = myNode;
list->size++;
}
/***************************************
* 函数功能:输出链表
* 函数参数:struct myHead* list待输出链表
* 函数返回值:无
****************************************/
void outPutList(struct myHead* list)
{
struct myNode *p = list->element->next;
while (p)
{
printf("%d ",p->data);
p = p->next;
}
}
到了这里,关于【数据结构与算法分析】反转链表与顺序表(内含源码,思路清晰)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!