线性表
线性表简介
线性表存储结构分为顺序存储结构 (顺序表) 和链式存储结构 (链表)。
顺序表
将“具有 ‘一对一’ 逻辑关系的数据按照次序连续存储到一整块物理空间上”的存储结构就是顺序存储结构。
使用顺序表存储数据之前,除了要申请足够大小的物理空间之外,为了方便后期使用表中的数据,顺序表还需要实时记录以下 2 项数据:
- 顺序表申请的存储容量;
- 顺序表的长度,也就是表中存储数据元素的个数;
提示:正常状态下,顺序表申请的存储容量要大于顺序表的长度。
顺序表初始化
自定义顺序表,C 语言实现代码如下:
typedef struct Table{
int *head; //声明一个名为 head 的长度不确定的数组(动态数组)
int length; //顺序表长度
int size; //实际表存储容量
}table;
接下来时初始化(赋值)顺序表。整个顺序表初始化的过程被封装到了一个函数中,此函数返回值是一个已经初始化完成的顺序表。
#define Size 5 //顺序表申请空间大小
table intTable(){
table t;
t.head = (int*)malloc(Size*sizeof(int)); //空表,动态申请空间
if(!t.head) //申请失败,退出
{
printf("init failed\n");
exit(0);
}
t.length = 0; //空表长度初始化为 0
t.size = Size; //初始存储空间 Size
return t;
}
实例
#include <stdio.h>
#include <stdlib.h>
#define Size 5
typedef struct Table{
int * head;
int length;
int size;
}table;
table initTable(){
table t;
t.head=(int*)malloc(Size*sizeof(int));
if (!t.head)
{
printf("初始化失败");
exit(0);
}
t.length=0;
t.size=Size;
return t;
}
//输出顺序表中元素的函数
void displayTable(table t){
for (int i=0;i<t.length;i++) {
printf("%d ",t.head[i]);
}
printf("\n");
}
int main(){
table t=initTable();
//向顺序表中添加元素
for (int i=1; i<=Size; i++) {
t.head[i-1]=i;
t.length++;
}
printf("顺序表中存储的元素分别是:\n");
displayTable(t);
return 0;
}
顺序表基本操作
基本操作:增删改查
插入
基本策略
- 遍历找到要插入的位置
- 将插入位置及其后续元素整体后移
- 插入元素
- 头插
- 中间插
- 尾插
//插入,elem 为插入的元素,add为插入位置
table addTable(table t, int elem, int add)
{
//判断插入位置是否合法,[1,length+1]
if(add > t.length || add < 1)
{
printf("插入位置非法\n");
return t;
}
//还需判断顺序表是否已满,若已满则需申请
if(t.length == t.size)
{
t.head = (int*)realloc(t.head, (t.size+1)*sizeof(int));
if(!t.head)
{
printf("realloc failed\n");
return t;
}
t.size+=1;
}
//后移
for(int i = t.length-1; i >= add-1; i--)
{
t.head[i+1] = t.head[i];
}
//插入
t.head[add-1] = elem;
t.length++;
return t;
}
删除
找到目标元素,后续元素前移
table delTable(table t, int add){
if(add > t.length || add < 1)
{
printf("删除位置非法\n");
return t;
}
for(int i = add; i <= t.length; i++)
{
t.head[i-1] = t.head[i];
}
t.length--;
return t;
}
查找
可以选择多多种查找算法实现,这里选择顺序查找算法
int sekectTable(table t, int elem){
for(int i = 0; i < t.length; i++)
{
if(elem == t.head[i])
{
return i+1;
}
}
return -1; //查找失败
}
修改
table updateTable(table t, int elem, int newElem){
int add = selectTable(t, elem);
if(add == -1)
{
printf("未查找到elem\n");
return t;
}
else
{
t.head[add-1] = newElem;
}
return t;
}
链表
链表的节点
链表中每个数据的存储都由以下两部分组成:
- 数据元素本身,其所在的区域称为数据域;
- 指向直接后继元素的指针,所在的区域称为指针域;
typedef struct Link{
char elem; //代表数据域
struct Link* next; //指针域
}link; //link 为节点名,每个节点都是一个link结构体
头节点,头指针,首元节点
一个完整的链表需要由以下几部分构成:
- 头指针:一个普通的指针,它的特点是永远指向链表第一个节点的位置。很明显,头指针用于指明链表的位置,便于后期找到链表并使用表中的数据;
- 节点:链表中的节点又细分为头节点、首元节点和其他节点:
- 头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;
- 首元节点:由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;
- 其他节点:链表中其他的节点;
链表初始化
创建一个链表需要做一下工作
- 声明一个头指针(如有必要,可以声明一个头节点)
- 创建多个存储数据的节点
实例:创建一个存储 {1,2,3,4} 的链表
//无头节点链表
link* initLink(){
link* p = NULL; //创建头指针
link* temp = (link*)malloc(sizeof(link)); //创建首元节点
//初始化首元节点
temp->elem = 1;
temp->next = NULL;
p = temp;
for(int i = 2; i < 5; i++)
{
link* a = (link*)malloc(sizeof(link));
a->elem = i;
a->next = NULL;
temp->next = a;
temp = temp->next;
}
return p;
}
//有头节点链表
link* initLink_head(){
link* p = (link*)malloc(sizeof(link)); //头节点
link* temp = p; //指针,用于遍历
for(int i = 1; i < 5; i++)
{
link *a = (link*)malloc(sizeof(link)); //创建节点并初始化
a->elem = i;
a->next = NULL;
//链接节点
p->next = a;
p = p->next;
}
return p;
}
链表的基本操作
增删改查,这里以有头节点的单链表
插入
链表插入元素的思想是固定的
- 将新节点的next指向下一个节点
- 将前一个节点的next指向新节点
//插入,p为原链表,elem为新数据,add为插入位置
link* insertLink(link* p, int elem, int add){
link* temp = p; //遍历用
//找到插入位置上一个节点
for(int i = 1; i < add; i++)
{
if(temp == NULL)
{
printf("插入位置非法\n");
return p;
}
temp = temp->next;
}
link* c = (link*)malloc(sizeof(link));
c->elem = elem;
c->next = temp->next;
temp->next = c;
return p;
}
// 在学校学的时候都要分头插,中间插入和尾插
删除
link* delLink(link* p, int add){
link* temp = p; //遍历
for(int i = 1; i < add; i++)
{
temp = temp->next;
}
link* del = temp->next; //单独设置一个指针指向被删除节点
temp->next = temp->next->next;
free(del); //手动释放该节点
return p;
}
//跟学校学的相比,少了防错,多了手动释放
查找
int selectLink(link* p, int elem){
link* temp = p;
int i = 1;
while(temp->next) //因为头节点
{
temp = temp->next;
if(t->elem == elem)
{
return i;
}
i++;
}
return -1;
}
修改
link* searchLink(link* p, int add, int elem){
link* temp = p->next;
for(int i = 1; i < add; i++)
{
temp = temp->next;
}
temp->elem = elem;
return p;
}
//这里查找的只是位置,如果查找某个元素就需要两个指针一起走
循环链表实现约瑟夫环
代码约瑟夫环:已知 n 个人(分别用编号 1,2,3,…,n 表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 开始,还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,直到圆桌上剩余一个人。
例题:假设此时圆周周围有 5 个人,要求从编号为 3 的人开始顺时针数数,数到 2 的那个人出列:
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
int number;
struct node* next;
}person;
person* initLink(int n){
person* head = (person*)malloc(sizeof(person));
head->number = 1;
head->next = NULL;
person* cyclic = head;
for(int i = 2; i <= n; i++)
{
person* body = (person*)malloc(sizeof(person));
body->number = i;
body->next = NULL;
cyclic->next = body;
cyclic = cyclic->next;
}
cyclic->next = head;
return head;
}
void findAndKick(person* head, int k, int m){
person* tail = head; //尾节点
while(tail->next != head)
{
tail = tail->next;
}
person* p = head; //遍历
while(p->number != k)
{
p = p->next;
}
while(p->next != p)
{
for(int i = 1; i < m; i++)
{
tail = p; //两个一起走,感觉没必要
p = p->next;
}
tail->next = p->next; //p->next = p->next->next
free(p);
p = tail->next;
}
}
双链表
代码#include <stdio.h>
typedef struct Line{
int data;
struct Line* pre;
struct Line* next;
}line;
line* initline(line* head){
head = (line*)malloc(sizeof(line));
head->data = 1;
head->pre = NULL;
head->next = NULL;
line* list = head;
for(int i = 2; i <= 3; i++)
{
line* body = (line*)malloc(sizeof(line));
body->pre = NULL;
body->next = NULL;
body->data = i;
list->next = body;
body->pre = list;
list = list->next;
}
return head;
}
line* insertLine(line* head, int data, int add){
line* temp = (line*)malloc(sizeof(line));
temp->data = data;
temp->pre = NULL;
temp->next = NULL;
if(add == 1)
{
temp->next = head;
head->pre = temp;
head = head->pre;
}
else
{
line* 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;
}
}
line* delLine(line* head, int data)
{
line* body = head;
while(body != NULL)
{
if(body->data == data)
{
body->pre->next = body->next;
body->next->pre = body->pre;
free(body);
return head;
}
body = body->next;
}
printf("can't find\n");
return head;
}
int searchLine(line* head, int data)
{
line* body = head;
int i = 1;
while(body != NULL)
{
if(body->data == data)
{
return i;
}
body = body->next;
i++;
}
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct line{
int No;
struct line* next;
}line;
typedef enum {
false,true
}bool;
void initLine(line** head,int n){
*head = (line*)malloc(sizeof(line));
(*head)->next = NULL;
(*head)->No = 1;
line* list = *head;
for(int i = 1; i < n; i++)
{
line* body = (line*)malloc(sizeof(line));
body->next = NULL;
body->No = i+1;
list->next = body;
list = list->next;
}
list->next = *head;
}
void display(line* head){
line* temp = head;
while(temp->next != head)
{
printf("%d",temp->No);
temp = temp->next;
}
}
int main(){
line* head = NULL;
srand((int)time(0));
int n, shootNum, round = 1;
printf("输入人数\n");
scanf("%d", &n);
initLine(&head, n);
line* lineNext = head;
while(head->next != head)
{
printf("第%d轮开始,从%d号开始\n",round,lineNext->No);
shootNum = rand()%n + 1;
printf("枪在第%d次会响\n", shootNum);
line* temp = lineNext;
for(int i = 1; i < shootNum - 1; i++)
{
temp = temp->next;
}
printf("%d 退出", temp->next->No);
line* del = temp->next;
temp->next = del->next;
if(del == head)
{
head = head->next;
}
free(del);
display(head);
lineNext = temp->next;
round++;
}
return 0;
}