1.1 顺序表
1.1.1顺序表的定义
采用顺序储存,用一组地址连续的存储单元存放数据元素。逻辑上相邻的数据元素,其物理位置也是相邻的。由于数组具有随机访问特性,所以通常用数组来描述顺序表。在C语言中,可用动态分配的一维数组表示顺序表。
// 顺序表的最大长度
#define MAXSIZE 100
// 表中数据元素的类型
typedef int ElemType;
// 定义顺序表类型
typedef struct {
// 存储空间的基地址
ElemType* elem;
// 表中实际存储的数据元素个数(当前表的长度
int length;
}SeqList;
1.1.2 初始化
使用malloc,为顺序表L动态分配一个预定义大小的数组空间,使elem指向这段空间的基地址。
Status InitList(SeqList &L) {
// 为顺序表分配一个大小为MAXSIZE的数组空间
L.elem=(ElemType *)malloc(sizeof(ElemType) * MAXSIZE);
// 分配失败,退出
if(!L.elem) exit(OVERFLOW);
// 空表的长度为0
L.length=0;
return 1;
}
1.1.3 基本操作
1.插入
//在顺序表L的第i个位置插入新元素e
bool ListInsert(SqList &L,int i,ElemType e) {
if(i<1||i>L.length+1){ //判断i的位置
return false;
}
if(L.length>=MaxSize){ //判断储存空间是否已满
return false;
}
for(int j=i-1;j<L.length;j++){
L.data[j+1]=L.data[j];
}
L.data[i-1]=e;
L.length++;
return true;
}
2.删除
//删除顺序表L中第i个位置的元素,并将被删除的元素用引用变量e返回
bool ListDelete (SqList &L,int i,ElemType &e){
if(i<=1||i>L.length){
return false;
}
e=L.data[i-1];
for (int j=i;j<L.length;j++){
L.data[j-1]=L.data[j];
}
L.length--;
return true;
}
3.按值查找
//查找第一个元素值等于e的元素,并返回其位序
int LocalElem(SqList L,ElemType e){
int i;
for(i=0;i<L.length;i++)
if(L.data[i]==e)
return i+1;
return 0;
}
以上三个操作的时间复杂度均为O(n)
1.2 链表
1.单链表及其操作
#include <stdio.h>
typedef struct node_
{
int data;
struct node_* next;
} Node;
//创建表头
Node* creatList()
{
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
//返回表头地址
return head;
}
//创建结点
Node* creatNode(int data)
{
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
//头插法
void insertNodeByHead(Node* head, int data)
{
//创建插入的节点,创建节点的同时对其数据域赋值
Node* newNode = creatNode(data);
newNode->next = head->next;//newNode—>next ==NULL
head->next = newNode;
}
//尾插法
void insertNodeByTail(Node* head, int data)
{
Node* newNode = creatNode(data);
//找到表尾
Node* tail = head;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newNode;
}
//删除头结点
void deleteNodeByHead(Node* headNode)
{
Node* deleteNode = headNode->next;
headNode->next = deleteNode->next;
free(deleteNode);
deleteNode = NULL;
}
//删除尾结点
void deleteNodeByTail(Node* head)
{
Node* tail = head;
Node* tailFront = NULL;
while (taile->next != NULL)
{
tailFront = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
tailFront->next = NULL;
}
//修改
void changeNode(Node* head, int changeData, int posData)
{
Node* p = head->next;
if (p == NULL)
{
printf("链表为空.\n");
return;
}
else
{
while (p->data != posData)
{
p = p->next;
if (p == NULL)
{
printf("没有找到该数据.\n");
return;
}
}
p->data = changeData;
}
}
//查找
void findNode(Node* head, int findData)
{
Node* p = head;
printf("请输入您要查询的数据:\n");
scanf("%d", &findData);
if (p->next == NULL)
{
printf("链表为空!\n");
}
else
{
while (p->data != findData)
{
p = p->next;
if (p->next == NULL)
{
printf("没有找到该数据!\n");
return;
}
}
printf("目标数据:%d存在于链表中!\n", p->data);
}
}
//打印
void printList(Node* headNode)
{
Node* p = headNode->next;
while (p)
{
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
2.双链表及其操作
//结点类型
typedef struct DNode{
int data; //数据域
struct DNode *pre,*next; //前驱和后继指针
}DNode, *DLinkList;
//初始化
void InitList(DLinkList &L){
L = (DNode *)malloc(sizeof(DLinkList));
L->pre = NULL;
L->next = NULL;
}
//头插法建立双链表,在头结点后插入新结点
DLinkList HeadInsert(DLinkList &L){
InitList(L); //初始化
int x;
cin>>x;
while(x!=9999){
DNode *s = (DNode *)malloc(sizeof(DNode));
s->data = x;
if(L->next == NULL){
s->next = NULL;
s->pre = L;
L->next = s;
}else{
s->next = L->next;
L->next->pre = s;
s->pre = L;
L->next = s;
}
cin>>x;
}
return L;
}
//尾插法建立双链表,在双链表的尾部插入新结点,因此,应该声明一个尾指针r并始终指向尾结点。
DLinkList TailInsert(DLinkList &L){
InitList(L);
DNode *s,*r=L;
int x;
cin>>x;
while(x!=9999){
s = (DNode *)malloc(sizeof(DNode));
s->data = x;
s->next = NULL;
s->pre = r;
r->next = s;
r = s;
cin>>x;
}
return L;
}
//求双链表的长度
int Length(DLinkList L){
DNode *p = L->next;
int len = 0;
while(p){
len++;
p = p->next;
}
return len;
}
//按值查找:查找x在L中的位置,此处与单链表相同,用不到pre
DNode *LocateElem(DLinkList L, int x){
DNode *p = L->next;
while(p && p->data != x){
p = p->next;
}
return p;
}
//按位查找:查找在双链表L中第i个位置的结点,此处与单链表相同,用不到pre
DNode *GetElem(DLinkList L, int i){
int j=1;
DNode *p = L->next;
if(i==0) return L;
if(i<1) return NULL;
while(p && j<i){
p = p->next;
j++;
}
return p; //如果i大于表长,p=NULL
}
//删除,将双链表中的第i个结点删除
void Delete(DLinkList &L, int i){
if(i<1 || i>Length(L)){
cout<<"delete failed"<<endl;
return;
}
DNode *p = GetElem(L,i-1);
DNode *q = p->next;
p->next = q->next; //1
q->next->prior = p; //2
free(q);
}
//判断是否为空
bool Empty(DLinkList L){
if(L->next == NULL){
cout<<"L is null"<<endl;
return true;
}else{
cout<<"L is not null"<<endl;
return false;
}
}
//遍历
void PrintList(DLinkList L){
DNode *p = L->next;
while(p){
cout<<p->data<<" ";
p = p->next;
}
cout<<endl;
}
3.循环链表
与将单链表相比,单向循环终点结点的指针域由 空指针 改为 指向头结点,使整个链表形成一个环。若不是尾结点,则cur->next != head。其他操作基本与单链表一致。
与双链表相比,双向循环链表尾结点的next指向head,head的pre指向最后一个结点,构成一个环
4.静态链表
借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,与前面说的链表中的指针不同的是,这里的指针是结点的相对地址(数组下标),又称游标。和顺序表一样,静态链表要预先分配一块连续的内存空间。
具体实现可以看看这里(