线性结构是最常用、最简单的一种数据结构。而线性表是一种典型的线性结构。其基本特点是线性表中的数据元素是有序且是有限的。线性表是一种最简单的线性结构。
特性:
1、必存在唯一的一个“第一元素”、“最后元素”。
2、除第一、最后元素外,均有 唯一的后继、唯一的前驱。
3、线性表是一种相当灵活的数据结构,对线性表的数据元素可以访问、插入和删除。
#define MAX_SIZE 100
typedef struct {
int data[MAX_SIZE];
int length;
} SeqList;
两种定义方式
1、将L定义为SeqList类型的变量,如SeqList L
通过属性访问方式L.data[i-1]访问顺序表中序号为i的元素
2、将L定义为SeqList类型的指针变量,如SeqList * L
通过指针访问方式L->data[i-1]访问顺序表中序号为i的元素
void createList(SeqList *list, int n)
{
int i;
for (i = 0; i < n; i++)
{
scanf("%d", &list->data[i]);
}
list->length = n;
}
若L为空表,则返回TRUE,否则FALSE。
bool ListEmpty(SeqList L)
{
return L.length==0;
}
用e返回L中第i个元素的值。
int GetElem(SeqList L,int i,int* e)
{
if(L.length==0||i<1||L.length
返回元素序号
int LocateElem(SeqList list,int e)
{
int i = 0;
while( (i<=list.length) && (list.data[i]!=e) )
{
i++;
}
if(i<=list.length)
{
printf("%d",i+1);
return i+1; //返回序号
}
else
{
printf("未找到");
return -1;
}
}
void PutElem(SeqList *L, int i, int *e )
{
if((i<1) || (i>L->length))
{
printf("位置不在范围内\n");
return;
}
else
L->data[i-1]=*e;
}
ListInsert( &L, i, e )1≤i≤LengthList(L)+1,在 L 的第 i 个元素之前插入新的元素 e,L 的长度增1。
void insertElement(SeqList *list, int position, int element) {
if (position < 1 || position > list->length + 1) {
//printf("插入位置不在范围内\n");
return;
}
int i;
for (i = list->length - 1; i >= position - 1; i--) {
list->data[i+1] = list->data[i];
}
list->data[position-1] = element;
list->length++;
}
ListDelete(&L, i, &e) 1≤i≤LengthList(L) ,删除 L 的第 i 个元素,并用 e 返回其值,L 的长度减1。
void DelElement(SeqList*list,int position,int *element)
{
if (position < 1 || position > list->length + 1 ) {
//printf("删除位置不在范围内\n");
return;
}
int i;
*e=list->data[position-1]
for (i = position; i < list->length; i++) {
list->data[i-1] = list->data[i];
}
list->length--;
}
void printList(SeqList *list) {
int i;
for (i = 0; i < list->length; i++)
{
printf("%d ", list->data[i]);
}
printf("\n");
}
优点:无需为表示表中元素之间的逻辑关系而增加额外的存储空间。
可以快速的存取表中任一位置的元素。
缺点:插入和删除操作需要移动大量元素。
当线性表长度变化较大时难以确定存储空间的容量。
造成存储空间的“碎片”。
链式存储结构分为单链表,循环链表,双向链表和静态链表
typedef struct Node
{
//链表中的结点包括数据域和指针域两个域。
int data; //数据域用来存储节点的值,
struct Node* next; //指针域用来存储节点本身的信息连接关系,由后继指针指示其位置。
} Node,*LinkList;
LinkList L;//*L为单链表的头指针
LinkList和Node*同为结构指针类型
常用LinkList类型定义指针变量,常用Node*定义单链表中的结点类型
为什么要定义Node型指针?
定义了一个结构体Node,其中包含一个int类型的data成员和一个指向Node结构体类型的指针next成员。在定义链表时,我们通常使用指向Node结构体类型的指针来表示节点之间的连接关系。因此,将next定义为struct Node*类型是合适的。
如果将next定义为int类型,那么在表示链表节点之间的连接关系时会变得困难,因为int类型只能指向整数类型的数据,无法表示节点的连接关系。因此,为了正确表示链表结构,应该将next定义为指向Node结构体类型的指针。
void InitList(LinkList *L)
{
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
}
为什么L前要加*号?
在函数参数中,如果需要修改指针本身的值(即改变指针指向的地址),需要传递指针的指针。在这种情况下,需要使用一个额外的指针来存储指针的地址,因此需要在参数前加上一个星号。在这里,InitList函数需要初始化一个链表指针L,因此需要传递指向指针的指针。
LinkList CreateList(LinkList *L, int data[], int n)
{
Node* p = *L;
for (int i = 0; i < n; i++) {
Node* q = (LinkList)malloc(sizeof(Node));
q->elem = data[i];
q->next = p->next;
p->next = q;
}
return *L;
}
LinkList CreateList(LinkList *L, int elem[], int n)
{
Node* p = *L;
for (int i = 0; i < n; i++) {
Node* q = (LinkList)malloc(sizeof(Node));
q->data = elem[i];
q->next = p->next;
p->next = q;
p = q;
}
return *L;
}
int ListLength(LinkList L)
{
Node *p = L->next;
int j = 0;
while(p!=NULL)
{
p=p->next;
j++;
}
return j;
}
顺链头开始,计数器j初值为0
当前指针p指向链表的首元结点,p=L->next
p依次往后(j++)直到表尾(p==NULL)
ElemType Get_Elem(Linklist *L , int i)
{
int j ; Node *p;
p=L->next; j=1; //使p指向第一个结点
while (p!=NULL && jnext;
j++;
} //移动指针p , j计数
if (j!=i)
return(-32768) ;
else
return(p->data);// p为NULL 表示i太大; j>i表示i为0
}
LNode *Locate_Node(LinkList *L,int key)
{
Node *p=L–>next;
while ( p!=NULL&& p–>data!=key)
p=p–>next;
if (p–>data==key)
return p;
else
{
printf(“所要查找的结点不存在!!\n”);
retutn(NULL);
}
}
bool ListInsert_L(LinkList *L, int i, ElemType e)
{
Node *p=L;;j=0;
while(p&&jnext;
j++
}
if (!p || j > i-1)
return -1;
Node * q=(LinkList)malloc(sizeof(Node));
q->next=p->next;
q->data=e;
p->next=q;
return 1;
}
void DelList(LinkList *L,int n)
{
Node*p=(*L)->next;
while(p!=NULL&&p->next!=NULL)
{
if(p->data==p->next->data)
{
Node*temp=p->next;
//创建一个临时指针temp,
//指向当前节点p的下一个节点,即要删除的重复节点。
p->next=temp->next;
//将当前节点p的next指针指向要删除节点temp的下一个节点,
//相当于跳过了要删除的节点。
free(temp);
//释放掉要删除的节点temp的内存空间,防止内存泄漏。
}
else
p=p->next;//注意不要漏掉这行代码
}
}
void PrintList(LinkList L)
{
Node *current = L->next;
while(current != NULL)
{
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
首先将链表的第一个节点保存在指针p中,然后将链表的头节点指向p的下一个节点,即删除了p节点。最后释放p节点的内存空间,完成了一个节点的删除操作。循环执行这个过程直到链表为空,即所有节点都被删除,链表被清空。
void ClearList(LinkList*L)
{
while ((*L)->next != NULL)
{
Node *p= (*L)->next;
(*L)->next=p->next;
free(p);
}
}
数据结构_中国大学MOOC(慕课) (icourse163.org)