数据结构知识总结(一)——线性表之单链表与顺序表(C语言实现)
线性表:
线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储),但是把最后一个数据元素的尾指针指向了首位结点)。
以上是百度对线性表的介绍与定义,线性表,顾名思义,就是线式存储的一种数据结构。
需要注意的是我们说“线性”和“非线性”,只在逻辑层次上讨论,而不考虑存储层次,因此,链式存储的链表,栈,队列依然属于线性表。
线性表主要由顺序表示或链式表示。在实际应用中,常以栈、队列、字符串等特殊形式使用。我们首先看看顺序表,因为顺序表结构简单,具体代码就简单的做了创建、插入和删除,如果有感兴趣的,可以自己去实践一下别的,这里不再过多描述。
I. 顺序表:
如下图是顺序表的存储结构,显而易见的,这就是一个简单的顺序存储即数组结构。代码贴在最后,这里不再做展示。
II. 链表
链表可分单向链表,双向链表,循环单链表和循环双链表,但因为所有的链表的存储结构都相同只是结构体定义有些许不同,因此这里不再一一叙述,下面我们以单链表为例,看看单链表的一些常见算法。
这里以C语言为例,先看看链表的结构定义
typedef struct node
{
int data;
struct node *next;
} NODE,*PNODE;
创建任意长度的链表,这里采用的是尾插法(就是永远想最后位置添加结点元素)
void insert_Arbitrarily(PNODE S,int val)
{
PNODE P=(NODE*)malloc(sizeof(NODE));
P->data=val;
P->next=S->next;
S->next =P;
}
PNODE create_list_Arbitrarily()
{
PNODE S;
S=(PNODE)malloc(sizeof(NODE));
S->next =NULL;
int data;
printf("请输入链表数据,输入-1结束\n");
while(scanf("%d",&data),data!= -1)
{
insert_Arbitrarily(S,data);
}
return S;
}
接下来是合并2个链表,这也非常简单,直接看代码吧
PNODE megre(PNODE L1,PNODE L2)
{
NODE* S=L1->next;
while(S->next!=NULL)
{
S=S->next;
}
S->next =L2->next ;
return L1;
}
再看插入与删除链表结点的算法,这里借用图片给大家说明
这是示意图,下面我们看代码实现
//删除某一个节点
int delete_list(PNODE pHead)
{
int i;
PNODE s,p;
printf("请输入需要删除链表中的第几个节点:");
scanf("%d",&i);
s=GPSnode(i,pHead);
p=GPSnode(i-1,pHead);
p->next=s->next;
free(s);
return 1;
}
//插入节点操作
int add_list(PNODE pHead)
{
int number;
PNODE s,p;
s=(PNODE)malloc(sizeof(NODE));
printf("请输入需要插入的数据");
scanf("%d",&s->data);
printf("请输入要该节点作为链表的第几个节点:");
scanf("%d",&number);
p=GPSnode(number-1,pHead);
s->next=p->next;
p->next=s;
return 1;
}
然后我们再看看单链表的遍历操作,这个很简单,不多说,直接上代码
//遍历输出链表
void traverse_list(PNODE pHead)
{
PNODE P=pHead->next;
while(P!=NULL)
{
printf("%d\t",P->data);
P=P->next;
}
return;
}
下面是单链表的逆置操作,由于个人表述能力有限,大家如果没看懂,请移步
单链表的逆置(头插法和就地逆置) by敲代码的小小哥
我们在这里重点介绍单链表原地逆置的迭代逆置
直接看图吧
按这步骤进行下去,最终结果为
下面我们来看代码
//就地逆置链表
PNODE reverse(PNODE head)
{
if(head==NULL||head->next==NULL)
return head;
NODE *p=NULL;
NODE *k=head->next;
while(k!=NULL)
{
NODE* temp=k;
k=k->next;
temp->next =p;
p=temp;
}
head->next=p;
return head;
}
看完了上面这些基础操作,链表的基础内容也就到此结束,而双链表和循环链表就是在单链表的基础上加了一个指针,这里不再赘述,谢谢大家!
下面贴上单链表实现的完整C语言代码
#include <stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node *next;
} NODE,*PNODE;
PNODE create_list(); //创建并初始化链表 (规定有多少元素时)
PNODE create_list_Arbitrarily(); //创建并初始化链表(任意长度)
void insert_Arbitrarily(PNODE S,int data); //头插入法
void traverse_list(PNODE pHead); //遍历链表
int delete_list(PNODE pHead); //删除链表结点
PNODE GPSnode(int i,PNODE pHead); //查找该节点,并返回该节点指针(这里是按位查找)
PNODE GPSnode_vale(int i,PNODE pHead); //查找该节点,并返回该节点指针(按值查找 )
PNODE reverse(PNODE head); //就地逆置链表
PNODE megre(PNODE L1,PNODE L2); //合并2个链表
int add_list(PNODE pHead); //插入结点
int length(PNODE pHead); //求长度
int show(); //选项界面
int main(void)
{
int i;
PNODE pHead=NULL;
pHead=create_list();
i=show();
switch(i)
{
case 1:add_list(pHead);break;
case 2:delete_list(pHead);break;
}
traverse_list(pHead);
printf("\n %d",length(pHead));
return 0;
}
//选项界面
int show()
{ int i;
printf("请选择菜单选项,完成操作");
printf("1.插入节点 2.删除节点");
scanf("%d",&i);
return i;
}
//创建并初始化链表 (规定有多少元素时)
PNODE create_list()
{
int len,i;
int val;
PNODE pHead=(PNODE)malloc(sizeof(NODE));
if(pHead==NULL)
{
printf("动态内存分配失败,即将退出程序\n");
exit(-1);
}
PNODE pTail=pHead;
pTail->next=NULL;
printf("请输入需要创建的链表节点数:");
scanf("%d",&len);
for(i=0;i<len;++i)
{
printf("请输入第%d个节点的数据: ",i+1);
scanf("%d",&val);
PNODE pNew=(PNODE)malloc(sizeof(NODE));
pNew->data=val;
pTail->next=pNew;
pNew->next=NULL;
pTail=pNew;
if(pNew==NULL)
{
printf("动态内存分配失败,即将退出程序!\n");
exit(-1);
}
}
return pHead;
}
创建并初始化链表(任意长度)
void insert_Arbitrarily(PNODE S,int val)
{
PNODE P=(NODE*)malloc(sizeof(NODE));
P->data=val;
P->next=S->next;
S->next =P;
}
PNODE create_list_Arbitrarily()
{
PNODE S;
S=(PNODE)malloc(sizeof(NODE));
S->next =NULL;
int data;
printf("请输入链表数据,输入-1结束\n");
while(scanf("%d",&data),data!= -1)
{
insert_Arbitrarily(S,data);
}
return S;
}
//遍历输出链表
void traverse_list(PNODE pHead)
{
PNODE P=pHead->next;
while(P!=NULL)
{
printf("%d\t",P->data);
P=P->next;
}
return;
}
//查找该节点,并返回该节点指针(这里是按位查找)
PNODE GPSnode(int i,PNODE pHead)
{ PNODE s;
s=pHead;
int x=0;
while(x!=i&&s!=NULL)
{
s=s->next;
x++;
}
if (s==NULL)
{
printf("未找到该节点");
return NULL;
}
else return s;
}
//查找该节点,并返回该节点指针(按值查找 )
PNODE GPSnode_vale(int i,PNODE pHead)
{
PNODE s;
s=pHead;
while(s!=NULL)
{
if(s->data==i)
return s;
else s=s->next;
}
if (s==NULL)
{
printf("未找到该节点");
return NULL;
}
}
//删除某一个节点
int delete_list(PNODE pHead)
{
int i;
PNODE s,p;
printf("请输入需要删除链表中的第几个节点:");
scanf("%d",&i);
s=GPSnode(i,pHead);
p=GPSnode(i-1,pHead);
p->next=s->next;
free(s);
return 1;
}
//插入节点操作
int add_list(PNODE pHead)
{
int number;
PNODE s,p;
s=(PNODE)malloc(sizeof(NODE));
printf("请输入需要插入的数据");
scanf("%d",&s->data);
printf("请输入要该节点作为链表的第几个节点:");
scanf("%d",&number);
p=GPSnode(number-1,pHead);
s->next=p->next;
p->next=s;
return 1;
}
//求链表长度 (不包括头结点)
int length(PNODE pHead)
{
PNODE P=pHead->next;
int length=0;
while(P!=NULL)
{
length++;
P=P->next;
}
return length;
}
//合并2个链表
PNODE megre(PNODE L1,PNODE L2)
{
NODE* S=L1->next;
while(S->next!=NULL)
{
S=S->next;
}
S->next =L2->next ;
return L1;
}
//就地逆置链表
PNODE reverse(PNODE head)
{
if(head==NULL||head->next==NULL)
return head;
NODE *p=NULL;
NODE *k=head->next;
while(k!=NULL)
{
NODE* temp=k;
k=k->next;
temp->next =p;
p=temp;
}
head->next=p;
return head;
}
然后这是最开始说的关于顺序表的代码,这个实现功能不多,只是起一个对比作用,望大家见谅。
#include<iostream>
#define datatype int
#define MaxSize 100
using namespace std;
struct SeqList
{
datatype val[MaxSize]; //这里就是存储元素的数组
int last; //表示顺序表的长度
};
SeqList* creat()
{
SeqList* S=new SeqList;
datatype num;
int i=0;
S->last=-1;
cout<<"请输入顺序表的初始数据"<<endl;
cout<<"输入-1表示输入结束"<<endl;
while(cin>>num&&num != -1)
{
S->val[i]=num;
i++;
S->last++;
}
return S;
}
//插入元素
void insert(SeqList *S,datatype data,int loc)
{
if(S->last<loc)
{
cout<<"越界"<<endl;
exit (-1);
}
loc-=1;
int i=loc;
datatype temp1,temp2;
temp1=S->val[i];
for(;i<=S->last;i++) //这里需要吧插入元素后面的所有元素都后移一位
{
temp2=S->val[i+1];
S->val[i+1]=temp1;
temp1=temp2;
}
S->val[loc]=data;
S->last++;
}
//删除元素
void _delete(SeqList *S,int loc)
{
if(S->last<loc)
{
cout<<"越界"<<endl;
exit (-1);
}
int i=S->last;
datatype temp1,temp2;
temp1=S->val[i];
for(;i>=loc;i--) //与插入一样,删除需要把之后的所有元素前移一位
{
temp2=S->val[i-1];
S->val[i-1]=temp1;
temp1=temp2;
}
S->last--;
}
//遍历输出顺序表
void print(SeqList *S)
{
int i=0;
while(i<=S->last)
{
cout<<S->val[i]<<" ";
i++;
}
cout<<endl;
}
//菜单操作
int choose(SeqList *S)
{
int number;
cout<<"1.插入一个数据"<<endl;
cout<<"2.删除一个数据"<<endl;
cout<<"按其他键退出"<<endl;
cout<<"请选择:";
cin>>number;
switch(number)
{
case 1:{
datatype data;
int loc;
cout<<"请输入要插入的元素以及插入的位置"<<endl;
cin>>data>>loc;
insert(S,data,loc);
break;
}
case 2: {
int loc;
cout<<"请输入要删除的元素的位置:";
cin>>loc;
_delete(S,loc);
break;
}
default: break;
}
}
int main()
{
SeqList *S=creat();
print(S);
choose(S);
print(S);
return 0;
}