线性表
基本概念
线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。数据元素是一个抽象的符号,其具体含义在不同的情况下一般不同。
在稍复杂的线性表中,一个数据元素可由多个数据项(item)组成,此种情况下常把数据元素称为记录(record),含有大量记录的线性表又称文件(file)。
线性表中的个数n定义为线性表的长度,n=0时称为空表。在非空表中每个数据元素都有一个确定的位置,如用ai表示数据元素,则i称为数据元素ai在线性表中的位序。
线性表的相邻元素之间存在着序偶关系。如用(a1,…,ai-1,ai,ai+1,…,an)表示一个顺序表,则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。当i=1,2,…,n-1时,ai有且仅有一个直接后继,当i=2,3,…,n时,ai有且仅有一个直接前驱 [1] 。
常用的线性表(我所知)
1、数组
数组在实际的物理内存上也是连续存储的,数组有上界和下界, 定义一个数组:
数组下标是从0开始的,a[0]对应第一个元素。其中,a[0]称为数组a的下界,a[6]称为数组a的上界。超过这个范围的下标使用数组,将造成数组越界错误。
特点
C语言中数组分为俩种,第一种是固定数组,这种数组在定义数组时就需要确定数组大小,第二种数组时动态数组,动态数组允许在运行时申请数组内存。(通过malloc来分配动态数组)
优点
1.无需为表示线性表中的逻辑关系而增加额外的存储空间。
2.可以快速的访问线性表中任一位置的元素。
缺点
1.插入和删除操作需要移动大量的元素。
2.难以确定线性表存储空间的容量。
3.造成存储空间的“碎片”,浪费存储空间。
代码:
动态分配数组
typedef int DataType
DataType *a;
int n;
scanf("%d",&n);//n是自己输入的动态数组的大小
a=(DataType*)malloc(sizeof(DataType)*n);//分配
2、链表
1)单向链表
单向链表是链表的一种。链表由节点所构成,节点内含一个指向下一个节点的指针,节点依次链接成为链表。因此,链表这种数据结构通常在物理内存上是不连续的。链表的通常含有一个头节点,头节点不存放实际的值,它含有一个指针,指向存放元素的第一个节点。
其节点包含
1.数据域:存贮该节点表示的元素的各种信息。
2.指针域:指向下一个节点的指针。
代码:
(1)链表结构:
typedef struct node
{
int data;
node *next;
}LNode,*LinkList;//Lnode是node的别名,LinkList是node*的别名
(2)创建链表(含头结点):
LinkList Creat()
{
LinkList list;
list = (LinkList)malloc(sizeof(LNode));
if (list == NULL)//创建头节点失败
{
printf("Error!\n");
return NULL;
}
return list;
}
(3)查找
bool Find(LinkList head, int data)//list 为传下来的链表 data为要查找的节点的data
{
LinkList list = head;
while (list != NULL)
{
if (list->data == data)
{
return true;
}
else
{
list = list->next;
}
}
return false;
}
(4)节点个数
int NodeNum(LinkList head)
{
LinkList list = head;
int count=0;//计数
while (list != NULL)
{
count++;
list = list->next;
}
return count;
}
(5)插入节点(头插法与尾插法)
LinkList InsertTailNode(LinkList head,int x)//尾插法
{
LinkList list = head;
if (head == NULL)
{
return NULL;
}
while (list->next != NULL)//找到插入位置(链表尾部)
{
list = list->next;
}
LinkList NewNode = (LinkList)malloc(sizeof(LNode));
NewNode->next = NULL;
NewNode->data = x;
list->next = NewNode;
return head;
}
LinkList InsertHeadNode(LinkList head, int x)//头插法
{
LinkList list = head;
if (head == NULL)
{
return NULL;
}
LinkList NewNode = (LinkList)malloc(sizeof(LNode));
NewNode->next = NULL;
NewNode->data = x;
if (list->next == NULL)
{
list->next = NewNode;
//return head;
}
else
{
NewNode->next = list->next;
list->next = NewNode;
}
return head;
}
(6)删除链表
bool DeleteNode(LinkList head, int x)
{
LinkList cur=head->next,pre=head;
if (cur||pre)
{
return false;
}
while (cur)
{
if (cur->data == x)
{
pre->next = cur->next;
free(cur);
return true;
}
else
{
pre = cur;//pre=pre->next;
cur = cur->next;
}
}
return false;
}
2、单向链表结构与顺序存储结构的优缺点
1. 顺序存储结构用一段连续的存储单元依次存储线性表的数据元素。
2.查找:顺序存储结构O(1),单链表O(n)。
3.插入和删除:顺序存储结构O(n),单链表O(1)。
4.顺序存储结构需要预先分配存储空间,分大了浪费空间,分小了容易造成内存溢出;单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制。
2)循环链表
将单链表中终端节点的指针由空指针改为指向头节点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环列表,简称循环列表(circular linked list)。循环列表解决了一个很麻烦的问题:如何从任意一个节点出发,访问到链表的全部节点。
其实循环列表和单链表的主要差异就在于循环的判断条件上,单链表是判断p->next是否为空,现在则是p->next不等于头结点,则循环未结束。