线性表
1.线性表的定义
1.线性表作为最简单的数据结构,通常为我们将n(n>=0)个元素的有限序列定义为线性表。我们将n=0时候线性表称为空表,注意哦,线性表是可以为空的,
线性表的第一个元素称之为表头(head),最后一个元素称之为表尾(tail)。
2.线性表的特点:
1.线性表是一个有限序列,每个元素有且只有一个直接前驱,有且只有一个直接后继(表头、表位节点除外)
2.数据类型相同
3.每一个元素都有一个固定的位置和值,位置又称之为下表,通常n个元素的下标为0-n-1
4.线性表中元素的值与它元素之间可以有特定的关系,也可以没有,例如有序表之间有特定的关系,元素的值按照递增顺序排列,无序表之间则没有这种关系。
1、顺序表
特点:直接存取方便,插入删除开销大。例如:CAarray,数组
下列是线性表示意图
(1)静态存储
#include<stdio.h>
#icnlude<stdlib.h>
#define maxSize 30
typedef int DateType;
typedef struct{
DateType date[maxSize];
int n;
}SeqList
(2)动态存储
#include<stdio.h>
#icnlude<stdlib.h>
#define maxSize 30
typedef int DateType;
typedef struct{
DateType *date;
int maxSize n;
}SeqList;
(3)动态分配顺序表的初始化函数
void initList(SeqList){
L.date=(DateType*)malloc(initList*sizeof(DataType));//malloc分配存储空间
if(!L.date){
printf("存储分配错误!\n");
exit(1);
L.maxsize=initSize;
L.n=0;
}
void clearList(SeqList& L){ //清空动态分配存储的顺序表(未撤销)
L.n=0; //顺序表当前长度置0
}
void Length(SeqList& L){
return L.n;
}
int Locate(SeqList& L,int i){
if(i>=1&&i<=L.n) return i-1;
else return -1;
}
}
(4)顺序表删除算法
int remove(SeqList& L,int i,DateType& x){
if(!L.n){
return false;
}
if(i<0||i>L.n){
return false;
}
x=L.date[i-1]; //被删除元素的值
for(int j=i;j<L.n;j++){
L.date[j-1]=L.date[i]; //删除的元素后续元素依次前移将被删除元素值覆盖
L.n--; //表长减一
return true;
}
}
(5)集合运算
#include <stdio.h>
#include <malloc.h>
#define INIT_SIZE 100
#define INCREM 10
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef struct SqList{
ElemType *slist;
int length;
int listsize;
}SqList;
int initList_sq(SqList *L){
L->slist=(ElemType*)malloc(INIT_SIZE*sizeof(ElemType));
if(!L->slist)return ERROR;
L->length=0;
L->listsize=INIT_SIZE;
return OK;
}
int createList_sq(SqList *L,int n){
int i;
for(i=0;i<n;i++)
scanf("%d",&L->slist[i]);
L->length=n;
return OK;
}
int insertList_sq(SqList *L,SqList *L2,SqList *L3){
int i=0,j=0,k=0;
while (i<L->length&&j<L2->length){
if(L->slist[i]<=L2->slist[j]) {
L3->slist[k++]=L->slist[i++];
}else {
L3->slist[k++]=L2->slist[j++];
}
}
while(i<L->length) {
L3->slist[k++]=L->slist[i++];
}
while(j<L2->length) {
L3->slist[k++]=L2->slist[j++];
}
L3->length=k;
return OK;
}
int deleteList_sq(SqList *L){
int i=0,j;
while(i<L->length)
{
while((L->slist[i]==L->slist[i+1])&&(i<L->length))
{
for(j=i;j<L->length;j++)
L->slist[j]=L->slist[j+1];
L->length--;
}
i++;
}
return OK;
}
void printList_sq(SqList *L){
int i;
for(i=0;i<L->length;i++)
printf("%d ",L->slist[i]);
}
int main(){
int n,n2;
SqList L;SqList L2;SqList L3;
initList_sq(&L);initList_sq(&L2);initList_sq(&L3);
scanf("%d%d",&n,&n2);
createList_sq(&L,n);createList_sq(&L2,n2);
insertList_sq(&L,&L2,&L3);
deleteList_sq(&L3);
printList_sq(&L3);
}
2、单链表
特点:存取不方便,插入删除,方便用一组任意的存储单元(此存储单元可以是连续的也可以是不连续的)来存储线性表的数据元素。通过指针指向逻辑上的下一个数据元素的地址,从而找到下一个元素。
用处:适用于需要经常插入删除的场合,例如操作系统的内存管理
(1)单链表的结构定义
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表结点类型
int data; //数据域,可以是别的各种数据类型,本文统一用int类型
struct LNode *next; //指针域
}LNode, *LinkList;
(2)单链表的初始化
void InitList(LinkList &L){
L = (LNode *)malloc(sizeof(LinkList));
L->next = NULL;
}
(3)单链表的建立
//头插法建立单链表
LinkList HeadInsert(LinkList &L){
InitList(L); //初始化
int x;
scanf("%d",&x);
while(x!=9999){ //输入9999表示结束
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scnaf("%d",&x);
}
return L;
}
//尾插法建立单链表
LinkList TailInsert(LinkList &L){
InitList(L);
LNode *s,*r=L;
int x;
scanf("%d",&x);
while(x!=9999){
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
s->data = x;
}
r->next = NULL;
return L;
}
(4)单链表的遍历
//遍历操作
void PrintList(LinkList L){
LNode *p = L->next;
while(p){
printf(" %d ",p->date);
p = p->next;
}
return 0;;
}
3、双向链表
特点:节点数据结构上增加一个指针域
用处:有倒序扫描链表的需求时
优点:双向链表有两个指针域,一个指向直接后继,另一个指向直接前驱。克服了单链表和循环链表只能顺时针向后查找其他节点的缺点。
(1)双向链表的定义
/*随机数的范围*/
#define MAX 100
/*节点结构*/
typedef struct Node{
struct Node *pre;
int data;
struct Node *next;
}Node;
(2)双向链表的创建
#define MAX 100
Node *CreatNode(Node *head)
{
head=(Node*)malloc(sizeof(Node));//鍒涘缓閾捐〃绗竴涓粨鐐癸紙棣栧厓缁撶偣锛?
if(head == NULL)
{
printf("malloc error!\r\n");
return NULL;
}
head->pre=NULL;
head->next=NULL;
head->data=rand()%MAX;
return head;
}
Node* CreatList(Node * head,int length)
{
if (length == 1)
{
return( head = CreatNode(head));
}
else
{
head = CreatNode(head);
Node * list=head;
for (int i=1; i<length; i++)
/*创建并初始化一个新结点*/
{
Node * body=(Node*)malloc(sizeof(Node));
body->pre=NULL;
body->next=NULL;
body->data=rand()%MAX;
/*直接前趋结点的next指针指向新结点*/
list->next=body;
/*新结点指向直接前趋结点*/
body->pre=list;
/*把body指针给list返回*/
list=list->next;
}
}
/*加上以下两句就是双向循环链表*/
// list->next=head;
// head->prior=list;
return head;
}
(3)双向链表的插入
表头表尾插入:只需建立相应的逻辑关系即可
表中插入:与前驱节点与后继节点都需要建立逻辑关系
/*在第add位置的前面插入data节点*/
Node * InsertListHead(Node * head,int add,int data)
{
/*新建数据域为data的结点*/
Node * temp=(Node*)malloc(sizeof(Node));
if(temp== NULL)
{
printf("malloc error!\r\n");
return NULL;
}
else
{
temp->data=data;
temp->pre=NULL;
temp->next=NULL;
}
/*插入到链表头,要特殊考虑*/
if (add==1)
{
temp->next=head;
head->pre=temp;
head=temp;
}
else
{
Node * body=head;
/*找到要插入位置的前一个结点*/
for (int i=1; i<add-1; i++)
{
body=body->next;
}
/*判断条件为真,说明插入位置为链表尾*/
if (body->next==NULL)
{
body->next=temp;
temp->pre=body;
}
else
{
body->next->pre=temp;
temp->next=body->next;
body->next=temp;
temp->pre=body;
}
}
return head;
}
/*在第add位置的后面插入data节点*/
Node * InsertListEnd(Node * head,int add,int data)
{
int i = 1;
/*新建数据域为data的结点*/
Node * temp=(Node*)malloc(sizeof(Node));
temp->data=data;
temp->pre=NULL;
temp->next=NULL;
Node * body=head;
while ((body->next)&&(i<add+1))
{
body=body->next;
i++;
}
/*判断条件为真,说明插入位置为链表尾*/
if (body->next==NULL)
{
body->next=temp;
temp->pre=body;
temp->next=NULL;
}
else
{
temp->next=body->pre->next;
temp->pre=body->pre;
body->next->pre=temp;
body->pre->next=temp;
}
return head;
}
4、循环链表
特点:最后的单元反过来指向第一个单元
用处:约瑟夫环问题