数据结构之线性表

线性表的抽象数据类型定义:

 

Date

线性表的数据对象集合为{A1, A2, ..., AN},每个元素的类型均为DateType 。其中除了第一个元素A1外,每一个元素有且只有一个直接前驱,除了最后一个元素AN外,每一个元素有且只有一个直接后继。数据元素之间的关系是一对一的关系。

operation
 	InitList(* L);//初始化操作,建立一个空的线性表
	ListEmpty(L);//若线性表为空,返回true,否则返回false
	ClearList(*L);//将线性表清空
	GetElem(L, i, *e);//将线性表L中的第i个位置元素返回给e
	LocateElem(L, e);//将线性表L中查找与给定值e相等的元素,如果查找成功返回该元素在表中序号表示;否则返回0表示失败
	ListInsert(*L, i, e);//在线性表L的第i个位置插入新元素e
	ListDelete(*L, i, *e);//删除线性表L中第i个元素,并用e返回其值
	ListLength(L);//返回线性表L中元素的个素
endADT
 

线性表的顺序存储结构

 

代码示例:

#include<stdio.h>

#define MAXSIZE 20	//存储空间初始化分配量
#define OK 1
#define ERROR 0

typedef int Status;	//函数类型,返回OK, ERROR结果状态码
typedef int ElemType;	//ElemType类型根据实际情况而定, 这里为int

typedef struct Person
{
	ElemType date[MAXSIZE];		//int类型数组,存储数据元素
	int length = -1;		//线性表当前长度
}SqList;

Status GetElem(SqList L, int i, ElemType* e)	//获得元素操作
{
	if (i < 0 || i >= L.length  || L.length == 0)
	{
		return ERROR;
	}
	*e = L.date[i];
	return OK;
}

Status ListInsert(SqList* L, int i, ElemType e)	//插入元素操作
{
	if (L->length == MAXSIZE)
	{
		return ERROR;
	}
	if (i < 0 || i >= MAXSIZE)
	{
		return ERROR;
	}
	if (i < L->length )	//插入数据不在表尾
	{
		for (int k = L->length; k >= i; k--)
		{
			L->date[k + 1] = L->date[k];
		}
	}
	else
	{
		L->date[i] = e;
	}

	L->date[i] = e;
	L->length++;
	return OK;
}

Status Pint_List(SqList* L)	//输出线性表
{
	int k = 0;
	for (int i = 0; i <= L->length; i++)
	{
		k = 1;
		printf("%d ", L->date[i]);
	}
	if (k == 1)
		return OK;
	else
		return ERROR;
}
Status ListDelete(SqList* L, int i, ElemType* e)	//删除线性表第 i 个元素
{
	if (L->length == -1)
		return ERROR;
	if (i < 0 || i > L->length)
		return ERROR;
	*e = L->date[i];
	if (i < L->length - 1)	//如果删除不是线性表最后位置
	{
		for (int k = i; k < L->length ; k++)
		{
			L->date[k] = L->date[k + 1];
		}

	}
	L->length--;
	return OK;
}

int main()
{
	SqList list1;
	for (int i = 0; i < 10; i++)
	{
		int insert_signal = ListInsert(&list1, i, i);//接受插入信号
		if (insert_signal == OK)
		{
			printf("%d 插入成功!\n",i);
		}
		else
		{
			printf("%d 插入失败!\n", i);
		}
	}
	 ListInsert(&list1, 10, 10);
	 ListInsert(&list1, 9, 10);
	 ListInsert(&list1, 1, 33);

	int print_signal = Pint_List(&list1);//接受输出信号
	if (print_signal == OK)
	{
		printf("输出成功!\n\n");
	}
	else
	{
		printf("输出失败!\n\n");
	}


	int temp_nal;
	int delete_signal = ListDelete(&list1,1, &temp_nal);
	if (delete_signal == OK)
	{
		printf("成功删除:%d\n\n", temp_nal);
		print_signal = Pint_List(&list1);
	}
	else
	{
		printf("删除失败!\n\n");
	}


	return 0;
}
 

优点:

  • 无须为表示表中元素之间的逻辑关系而增加额外的存储空间
  • 可以快速的存取表中任一位置的元素
 

缺点:

  • 插入和删除操作需要移动大量元素
  • 当线性表长度变化较大时,难以确定存储空间的容量
  • 造成存储空间的“碎片”
 

线性表的链式存储结构

 

代码示例:

#include<stdio.h>
#include<stdlib.h>


int num = 0;
typedef struct Node
{
	int value;
	struct Node *next;

}std;


std *create_list()	//创建一张空表
{
	std* head = NULL;
	return head;
}


std* Insert_list(std* head, int i, int elem)//插入操作
{
	std* p1 = NULL, *p2 = NULL ,* p3 = NULL;
	if (head == NULL)	//判断链表是否为空
	{
		p2 = (std*)(malloc(sizeof(std)));
		num++;
		p2->value = elem;
		p2->next = NULL;
		head = p2;
		return head;

	}

	if (i == 0 && head != NULL)	//头插
	{
		p2 = (std*)(malloc(sizeof(std)));
		num++;
		p2->value = elem;
		p2->next = head;
		head = p2;
		return head;

	}
	int j = 0;
	p1 = head;
	while (j < i && p1 != NULL)
	{
		p3 = p1;
		p1 = p1->next;
		j++;
	}

	if (p1 == NULL)	//尾插
	{
		p2 = (std*)(malloc(sizeof(std)));
		num++;
		p2->value = elem;
		p3->next = p2;
		p2->next = NULL;
		return head;
	}
	else   //中插
	{
		p2 = (std*)(malloc(sizeof(std)));
		num++;
		p2->value = elem;
		p3->next = p2;
		p2->next = p1;
		return head;
	}
}

std* Deletelist(std* L, int i, int* e)//删除操作
{
	int j = 0;
	std* p = L;
	std *p1 = NULL;
	if (i == 0)//头删
	{
		L = p->next;
		num--;
		free(p);
		return L;
	}
	while (j < i && p)
	{
		p1 = p;
		p = p->next;
		j++;
	}
	if (p->next == NULL)//尾删
	{
		p1->next = NULL;
		num--;
		return L;
	}
	else
	{
		p1->next = p->next;//中删
		num--;

	}

	free(p);
	return L;

}


std* modifylist(std* L, int i, int e)//修改操作
{
	std* p = L;
	int j = 0;
	while (j < i && p)
	{
		p = p->next;
		j++;
	}
	p->value = e;
	return L;
}

void queryelem(std* L, int elem)//查找操作
{
	std* p = L;
	while (p)
	{
		if (p->value == elem)
		{
			printf("找到此元素:%d", elem);
			return 0;
		}
		p = p->next;
	}
	printf("表中无此元素!");
}

void print_list(std* head)//打印链表
{
	if (head)
	{
		std* p = head;
		while (p)
		{
			printf("%d ", p->value);
			p = p->next;
		}
		printf("\n");
	}
	else
	{
		printf("链表为空!");
	}
}

int main()
{
	std* head = create_list();
	head = Insert_list(head, 0, 0);
	head = Insert_list(head, 1, 1);
	head = Insert_list(head, 2, 2);
	head = Insert_list(head, 3, 3);
	head = Insert_list(head, 4, 4);
	head = Insert_list(head, 5, 5);
	head = Insert_list(head, 6, 6);
	int a;

	print_list(head);
	printf("删除第一个元素:\n");
	head = Deletelist(head, 0, &a);
	print_list(head);

	printf("删除第三个元素:\n");
	head = Deletelist(head, 3, &a);
	print_list(head);

	printf("删除最后一个元素:\n");
	head = Deletelist(head, 4, &a);
	print_list(head);

	printf("修改第3个元素值为2\n");
	head = modifylist(head, 3, 2);
	print_list(head);

	printf("查找是否存在值为3的结点!\n");
	queryelem(head, 3);
	return 0;
}
 

优点:

  • 单链表在找出位置的指针后,插入和删除的时间复杂度仅为O(1)
  • 单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制
 

缺点:

单链表查找时间复杂度O(n)

 

循环链表

 

定义:

将单链表中终端结点的指针由空指针指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为循环链表

循环链表循环链表

 

双向链表

 

定义:

双向链表是在单链表的每一个结点中,在设置一个指向前驱结点的指针域

 

存储结构

typedef struct Node
{
	int value;
	struct Node* prior;
	struct Node *next;

}std;
 

插入操作:

双向插入双向插入

 

删除操作:

双向删除双向删除

posted @ 2022-08-20 23:49  sugeek  阅读(58)  评论(0)    收藏  举报