数据结构-线性表
-
线性表
线性表(其中一种数据结构,属于逻辑结构):具有相同特征的数据,数据之间是一种先后有序序列
具有线性关系的数据集合,一个数据前有另一个数据,在之后还有一个数据(对于一个数据来说,和它有直接关系的只有之前的这个数据(前驱),之后的一个数据(后继));同时第一个数据之前没有,最后一个数据之后也没有;把数据叫做元素线性表中每个元素只有一个前驱,一个后继
线性表能够实现的运算:
1、创建
2、清空
3、插入
4、删除
5、求表长
6、表为空
7、查询
8、修改图 《数据结构C语言版》page 6
顺序存储<------------>链式存储 数组<------------>指针
一个地址是连续的,一个是不连续的由malloc时随机分配地址 -
1,顺序表
顺序表:线性表的顺序存储(具备线性关系)(逻辑结构是线性结构,存储结构是顺序存储)
顺序表是线性表的一种(是线性表采用顺序存储的方式在计算机中表示)
顺序存储:开辟一段连续的内存空间,用来存储具备一种逻辑结构的数据集合在使用顺序表进程存储时,这段连续空间地址的大小就是线性表的元素序列的顺序(存储的地址的高低就是序列前驱后继的顺序),地址就能表示线性表(顺序表)的关系
顺序表中,需要提前指定连续空间的大小,通过下标的值能够表示前驱后继,通常需要有一个整数表示顺序表最后一个元素位置顺序表实现的运算和线性表一样
//数据结构形式 #define SIZE 10 typedef int data_t; typedef struct array { data_t data[SIZE];//size整段連續空間的長度 int length;//順序表當前存儲數據的長度 }ARRAY, *ARRAYP; //增删改查 ARRAYP create_array(); void clear_array(ARRAYP p); void insert_array(ARRAYP p,int pos,data_t num); void delete_array(ARRAYP p,int pos); int empty_array(ARRAYP p); int full_array(ARRAYP p); data_t search_array(ARRAYP p,int pos); void update_array(ARRAYP p,int pos,data_t num); void show_array(ARRAYP p);
具体实现
//创建 ARRAYP create_array() { ARRAYP p = malloc(sizeof(ARRAY));//申请空间 if(p =\= NULL) { printf("malloc error\n"); return NULL; } p->length = 0;//初始化长度 bzero(p->data,sizeof(data_t)*SIZE);//清空 return p; } //清空 void clear_array(ARRAYP p) { if(p == NULL) { printf("struct array is NULL\n"); return; } bzero(p->data,sizeof(data_t)*SIZE); p->length = 0; }
//插入
void insert_array(ARRAYP p,int pos,data_t num)
{
//判断是否存在
if(p == NULL)
{
printf("array is NULL\n");
return ;
}
//判满
if(full_array(p))
{
printf("array is full\n");
return ;
}
//判断插入位置是否越界
if((p->length+1) < pos)
{
printf("insert pos error\n");
return ;
}
int i;
//将插入位置后的数据全部向后移动一位
for(i = p->length ; i > pos-1 ; i--)
{
p->data[i] = p->data[i-1];
}
//插入数据
p->data[pos-1] = num;
p->length++;
}
//删除
void delete_array(ARRAYP p,int pos)
{
//判断删除位置是否越界
if(pos > p->length)
{
printf("delete pos is error\n");
return ;
}
//判空
if(empty_array(p))
{
printf("delete is empty\n");
return ;
}
int i;
//将删除位置之后的数据依次向前移动
for(i = pos-1;i < p->length-1; i++)
{
p->data[i] = p->data[i+1];
}
p->length--;
}
//判断是否为空
int empty_array(ARRAYP p)
{
return p->length =\= 0?1:0;
}
6、判断是否为满
int full_array(ARRAYP p)
{
return p->length == SIZE?1:0;
}
//遍历整个数组
data_t search_array(ARRAYP p,int pos)
{
if(pos > p->length)
{
printf("search pos is error\n");
return 0;
}
return p->data[pos-1];
}
//找到位置,直接修改值
void update_array(ARRAYP p,int pos,data_t num)
{
if(pos > p->length)
{
printf("update pos is error\n");
return ;
}
p->data[pos-1] = num;
}
//遍历并显示
void show_array(ARRAYP p)
{
int i;
for(i = 0;i < p->length;i++)
{
printf("%d ",p->data[i]);
}
printf("\n");
}
-
2.链表
图《C和指针》 page 235
链表:线性表的离散(链式)存储
把线性表中的所有元素存储在不同的区域,通过指针或地址建立数据元素之间的关系,把这种线性表叫做链表离散(链式)存储:在进行存储时每个元素存放位置相互之间都是没有直接联系的,内存开辟都是单独的一个数据进行,单独的一个个数据分开进行存储,元素的地址不一定连续,地址之间不是表示数据关系的
在链表中,每个元素不只是存储数据还应该存储下一个元素的地址,人为的创造数据之间的关系
链表中数据元素(数据元素和指针)-------结点链表中的每个结点是单独创建,只需要知道结点的结构就能知道链表的样式
①单链表
单链表:链表中的结点只有一个指针,这个指针就一定要表示数据结点的关系,需要指向结点的后继(表示有序序列)typedef int data_t; typedef struct node { data_t data;//数据域 struct node * next;//指針域 }NODE,*NODEP; NODEP create_list(); void clear_list(NODEP head); void insert_list(NODEP head,int pos,data_t num); void delete_list(NODEP head,int pos); int search_list(NODEP head,data_t num); void update_list(NODEP head,int pos,data_t num); void show_list(NODEP head); int empty_list(NODEP head); //创建链表 NODEP create_list() { NODEP head = malloc(sizeof(NODE));//创建头节点 head->next = NULL;//head->data头结点不能被操作 return head; }
//插入节点 void insert_list(NODEP head,int pos,data_t num) { NODEP p = head; int i = 0; while(p->next != NULL) { i++;//用来计算位置 if(i =\= pos)//判断是否达到要插入的位置 { break; } p = p->next; } NODEP q = malloc(sizeof(NODE)); //申请一个节点 if(q == NULL) { printf("insert malloc error\n"); return ; } //进行插入操作的插入操作的指针交换 如上图所示 q->data = num; q->next = p->next; p->next = q; }
//删除节点 void delete_list(NODEP head,int pos) { if(head =\= NULL) { printf("list is erorr\n"); return ; } if(empty_list(head)) { printf("list is empty\n"); return; } NODEP p = head; int i = 0; while(p->next != NULL) { i++; //找到pos并释放 if(i =\= pos) { NODEP q = p->next; p->next = q->next;//指针交换 printf("delete num is %d\n",q->data); free(q);//释放空间 break; } p = p->next; } } //遍历 找到num 返回节点位置 否则返回0 int search_list(NODEP head,data_t num) { NODEP p = head->next; int i = 0; while(p != NULL) { i++; if(p->data =\= num) { return i; } p = p->next; } return 0; } //更新节点 void update_list(NODEP head,int pos,data_t num) { NODEP p = head; int i = 0; while(p->next!= NULL)//判断下一个节点是否存在 { i++; p = p->next; if(i =\= pos)//找到节点并修改值 { p->data = num; break; } } } void show_list(NODEP head) { NODEP p = head->next; while(p != NULL)//表示節點存在 { printf("%d ",p->data); p = p->next; } printf("\n"); } int empty_list(NODEP head) { return head->next == NULL?1:0; } void clear_list(NODEP head) { NODEP p = head->next; while(p != NULL) { NODEP q = p; p = p->next; free(q); } head->next = NULL; }
②双链表
图《C和指针》 page 245
typedef struct NODE{
struct NODE *fwd;//指针域
struct NODE *bwd;
int value;//数据域
} Node;
//插入节点 void insert_list(NODEP head,int pos,data_t num) { NODEP p = head; int i = 0; while(p->next != NULL) { i++;//计数 if(i =\= pos)//找到位置 { break; } p = p->next; } NODEP q = malloc(sizeof(NODE)); //申请节点空间 if(q == NULL) { printf("insert malloc error\n"); return ; } //指针交换 q->data = num; NODEP G = p->next; q->next = p->next; q->forward = p; if(p->next != NULL) { G->forward = q; //p->next->forward = q; } p->next = q; }
//删除节点 void delete_list(NODEP head,int pos) { if(head =\= NULL) { printf("list is erorr\n"); return ; } if(empty_list(head)) { printf("list is empty\n"); return; } NODEP p = head; int i = 0; while(p->next != NULL) { i++; if(i == pos) { NODEP q = p->next; p->next = q->next; q->next->forward = p; printf("delete num is %d\n",q->data); free(q); break; } p = p->next; } }
③单向循环链表
图《数据结构C语言版》 page 35
单链表中最后一个结点的next指针域不在是为空,用来存储第一个结点的地址,形成循环④双向循环链表
图《数据结构C语言版》 page 36
NODE * create() { //空双向循环列表 NODE * head = malloc(sizeof(NODE)); head->forward = head; head->next = head; return head; } void insert(NODE * head,int num) { NODE * q = malloc(sizeof(NODE)); q->data = num; NODE * p = head->forward; q->next = p->next; q->forward = p; p->next->forward = q; p->next = q; } void delete(NODE * head) { NODE * p = head; NODE * q = p->next; p->next = q->next; p->next->forward = p; free(q); }
2.堆栈 队列
1.栈 Last-In First-Out LIFO
栈:是一种线性表,是只允许在表中一端进行插入删除操作,另一端不能进行任何操作的线性表
栈特点:是一种先进后出的线性表
能够进行操作的一端叫做栈顶,不能进行操作的一端叫做栈底,进行插入操作叫做入栈(压栈),进行删除操作叫做出栈(弹栈)
在栈中,存在一个栈顶指针指向当前的栈顶位置,通过固定栈底位置去实现,在栈中栈顶栈底的位置是有可能发生变换,是一种动态的过程
①顺序存储(顺序栈)
在顺序栈中top指针指向的位置不同它的代码方式也是不同,一开始top指向栈中栈底第一个数据位置(top==0,这是可以作为判空条件)但编写的代码一定按照top指针指向位置为空数据进行判断
也可以(top == -1作为判空条件),top指向的位置里面一定是存储了数据
#define SIZE 10
typedef int data_t;
typedef struct node
{
data_t data[SIZE];//size整段連續空間的長度
int length;//順序表當前存儲數據的長度
}NODE, *NODEP;
NODEP create_stack()
{
NODEP p = malloc(sizeof(NODE));
if(p == NULL)
{
printf("malloc error\n");
return NULL;
}
p->length = 0;
bzero(p->data,sizeof(data_t)*SIZE);
return p;
}
//压栈
void push_stack(NODEP top, data_t num)
{
if(full_stack(top))
{
printf("栈区已满\n");
return ;
}
top->data[top->length]=num;
top->length++;
}
//出栈
void pop_stack(NODEP top)
{
if(empty_stack(top))
{
printf("栈区已空\n");
return ;
}
top->length--;
printf("清空栈顶%d",top->data[top->length]);
}
②链式存储(链式栈)
栈中数据之间是离散的,所以使用指针来找到数据之间的关系;每个结点都有两个区域(数据域、指针域)
在链式栈进行入栈出栈操作时,新的栈顶要存放原先栈顶的地址
链式栈的操作就是链表的头插法
typedef struct node
{
int data;
struct node * next;
}NODE,*NODEP;
NODEP create_stack();
void push_stack(NODEP top,int data);
void pop_stack(NODEP top);
int empty_stack(NODEP top);
int top_stack(NODEP top);
void show_stack(NODEP top);
NODEP create_stack()
{
NODEP top = malloc(sizeof(NODE));
if(top == NULL)
{
printf("create stack error\n");
return NULL;
}
top->next = NULL;
return top;
}
//压栈
void push_stack(NODEP top,int data)
{
if(top == NULL)
{
return ;
}
NODEP p = malloc(sizeof(NODE));
p->data = data;
//在沒有進行入棧操作前,當時的棧頂指針中存儲的是當時的棧頂元素,所以需要先把原棧頂元素的地址保存在新的棧頂元素的next指針域
p->next = top->next;
top->next = p;
}
void pop_stack(NODEP top)
{
if(top == NULL)
{
return ;
}
if(empty_stack(top))
{
printf("stack is empty\n");
return ;
}
NODEP p = top->next;
top->next = p->next;
printf("pop data is %d\n",p->data);
free(p);
}
int empty_stack(NODEP top)
{
return top->next == NULL?1:0;
}
int top_stack(NODEP top)
{
if(empty_stack(top))
{
printf("stack is empty\n");
return -1;
}
return top->next->data;
}
void show_stack(NODEP top)
{
NODEP p = top->next;
while(p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
2.队列
特点:先进先出 First-In First-Out FIFO
队列(线性表):是一种特殊线性表,限制在线性表的两端进行操作(插入、删除);其中一端进行删除操作,另一端进行插入操作,把删除的一端叫做队头,把删除操作叫做出队,把插入的一端叫做队尾,把插入操作叫做入队。是一种先进先出的特点FIFO
队列进行操作时,由于设置的是在表的两端,所以需要两个指针分别指向表的两端(队头、队尾)
队头指针
队尾指针
队列中,进行删除操作时,每个队头结点需要找到下一次队头结点(新的队头结点);进行插入操作时,原队尾结点能够获取到新队尾结点的地址
①链式存储(链式队列)
链式队列其实就是添加了尾指针的链表
typedef struct node
{
int data;
struct node * next;
}NODE,*NODEP;
typedef struct point
{
NODEP front;
NODEP rear;
}linkqueue,*linkqueueP;
linkqueueP create_queue();
void insert_queue(linkqueueP p,int data);
void delete_queue(linkqueueP p);
int empty_queue(linkqueueP p);
void show_queue(linkqueueP p);
linkqueueP create_queue()
{
linkqueueP p = malloc(sizeof(linkqueue));
p->front = p->rear = malloc(sizeof(NODE));
p->front->next = NULL;
return p;
}
void insert_queue(linkqueueP p,int data)
{
NODEP q = malloc(sizeof(NODE));
q->data = data;
q->next = NULL;
p->rear->next = q
p->rear = q;
}
void delete_queue(linkqueueP p)
{
if(empty_queue(p))
{
printf("queue is empty\n");
return;
}
NODEP q = p->front->next;
p->front->next = q->next;
printf("delete data is %d\n",q->data);
if(q->next == NULL)
{
p->rear = p->front;
}
free(q);
}
int empty_queue(linkqueueP p)
{
return p->front->next == NULL?1:0;
}
void show_queue(linkqueueP p)
{
NODEP q = p->front;
while(q->next != NULL)
{
q = q->next;
printf("%d ",q->data);
}
printf("\n");
}
②顺序存储(顺序队列–循环队列)
队头指针和队尾指针如果指向相同的一个位置,作为判断这个队列为空的条件,这时:
队尾指针的含义,即将在这个位置插入数据
队头指针的含义,即将要在这个位置删除数据
typedef struct queue
{
int *data;//用來存儲一段連續空間的首地址
int length;//用來存儲數據的個數
int front;//隊頭指針
int rear;//隊尾指針
}SEQUEUE;
SEQUEUE * create_queue();
void insert_queue(SEQUEUE * queue,int data);
void delete_queue(SEQUEUE * queue);
int empty_queue(SEQUEUE * queue);
int full_queue(SEQUEUE * queue);
void show_queue(SEQUEUE * queue);
,
SEQUEUE * create_queue()
{
SEQUEUE * queue = malloc(sizeof(SEQUEUE));
scanf("%d",&queue->length);
queue->data = malloc(sizeof(int)*(queue->length));//自定义队列的长度
queue->front = queue->rear = 0;
return queue;
}
//入队操作
void insert_queue(SEQUEUE * queue,int data)
{
if(full_queue(queue))
{
printf("queue is full\n");
return;
}
queue->data[queue->rear] = data;
//队尾指针移动,+1%是为了实现循环
queue->rear = (queue->rear+1)%queue->length;
}
void delete_queue(SEQUEUE * queue)
{
if(empty_queue(queue))
{
printf("queue is empty\n");
return ;
}
printf("front data is %d\n",queue->data[queue->front]);
//对头指针移动
queue->front = (queue->front+1)%queue->length;
}
int empty_queue(SEQUEUE * queue)
{
//如果对头指针指向相同地址,则为最初状态,队列为空
return queue->front == queue->rear?1:0;
}
int full_queue(SEQUEUE * queue)
{
//如果队尾指针+1和对头指针相同,则表示堆满
return queue->front == (queue->rear+1)%queue->length?1:0;
}
void show_queue(SEQUEUE * queue)
{
int i = queue->front;
while(i != queue->rear)
{
printf("%d ",queue->data[i]);
i++;
}
printf("\n");
}