分文件编程实现:
main.c
#include <stdio.h>
#include <stdlib.h>
#include "seqlist.h"
int main(int argc, char const *argv[])
{
seqlist_p p = CreateEpSeqlist();
InsertIntoSeqlist(p, 0, 10);
InsertIntoSeqlist(p, 1, 20);
InsertIntoSeqlist(p, 2, 30);
ShowSeqlist(p);
DeleteIntoSeqlist(p, 1);
ShowSeqlist(p);
ChangePostSeqList(p, 0, 99);
ShowSeqlist(p);
printf("search 99:%d\n", SearchDataSeqList(p, 99));
return 0;
}
seqlist.c
#include <stdio.h>
#include <stdlib.h>
#include "seqlist.h"
// 创建一个空的顺序表
seqlist_p CreateEpSeqlist() //create创造 empty 空的 seqlist顺序表
{
//动态申请一块顺序表结构体大小空间
seqlist_p p = (seqlist_p)malloc(sizeof(seqlist_t));
if (NULL == p)
{
perror("malloc lost"); //perror打印上一个函数报的错误信息
return NULL; //错误情况让函数返回空指针
}
//对结构体初始化
p->last = -1;
return p;
}
//判断顺序表是否为满,满返回1,未满返回0
int IsFullSeqlist(seqlist_p p) //full满
{
return p->last + 1 == N;
}
//向顺序表的指定位置插入数据
int InsertIntoSeqlist(seqlist_p p, int post, int data)
{
//容错判断:判满,对post做判断
if (IsFullSeqlist(p) || post < 0 || post > p->last + 1)
{
printf("InsertIntoSeqlist err\n");
return -1; //错误返回
}
//让最后一个元素到插入位置的元素向后移动一个单位
for (int i = p->last; i >= post; i--)
p->data[i + 1] = p->data[i];
//插入数据
p->data[post] = data;
//让last加一
p->last++;
return 0;
}
//遍历顺序表sequence顺序list表
void ShowSeqlist(seqlist_p p)
{
for (int i = 0; i <= p->last; i++)
printf("%d ", p->data[i]);
printf("\n");
}
//判断顺序表是否为空,为空返回1,不为空返回0
int IsEpSeqlist(seqlist_p p)
{
return p->last == -1;
}
//删除顺序表中指定位置的数据,post为删除位置
int DeleteIntoSeqlist(seqlist_p p, int post)
{
//容错判断:判空,对post判断
if (IsEpSeqlist(p) || post < 0 || post > p->last)
{
printf("DeleteIntoSeqlist err\n");
return -1;
}
//从删除位置元素的后一个元素到最后一个元素向前移动一个单位
for (int i = post + 1; i <= p->last; i++)
p->data[i - 1] = p->data[i];
//让last减一,因为删除了一个元素有效元素少了一个
p->last--;
return 0;
}
// 修改指定位置上数据
int ChangePostSeqList(seqlist_p p, int post, int data)
{
//容错判断:判空,对post判断
if (IsEpSeqlist(p) || post < 0 || post > p->last)
{
printf("ChangePostSeqList err\n");
return -1;
}
//修改指定位置数据
p->data[post] = data;
return 0;
}
// 查找指定数据出现的位置,返回下标,未找到返回-1
int SearchDataSeqList(seqlist_p p, int data)
{
for (int i = 0; i <= p->last; i++)
{
if (p->data[i] == data)
return i;
}
return -1;
}
// 清空顺序表(清空:访问不到,但是内存中还有。销毁:内存清空)
void ClearSeqList(seqlist_p p)
{
p->last = -1;
}
seqlist.h
#ifndef __SEQLIST_H__
#define __SEQLIST_H__
#define N 10
typedef struct seqlist //封装顺序表结构体类型
{
int data[N]; //用来存数据的数组
int last; //代表数组中最后一个有效元素的下标
} seqlist_t, *seqlist_p;
// 创建一个空的顺序表
seqlist_p CreateEpSeqlist(); //create创造 empty 空的 seqlist顺序表
//判断顺序表是否为满,满返回1,未满返回0
int IsFullSeqlist(seqlist_p p); //full满
//向顺序表的指定位置插入数据
int InsertIntoSeqlist(seqlist_p p, int post, int data);
//遍历顺序表sequence顺序list表
void ShowSeqlist(seqlist_p p);
//判断顺序表是否为空,为空返回1,不为空返回0
int IsEpSeqlist(seqlist_p p);
//删除顺序表中指定位置的数据,post为删除位置
int DeleteIntoSeqlist(seqlist_p p, int post);
// 修改指定位置上数据
int ChangePostSeqList(seqlist_p p, int post, int data);
// 查找指定数据出现的位置,返回下标,未找到返回-1
int SearchDataSeqList(seqlist_p p, int data);
// 清空顺序表(清空:访问不到,但是内存中还有。销毁:内存清空)
void ClearSeqList(seqlist_p p);
#endif
makefile
CC=gcc
GFLAGS=-c -g -Wall
OBJS=seqlist.o main.o
seqlist:$(OBJS)
$(CC) $^ -o $@
%.o:%.c
$(CC) $(GFLAGS) $< -o $@
.PHONY:clean
clean:
$(RM) seqlist *.o
顺序表特点:
- 顺序表内存空间连续
- 顺序表长度固定
- 插入和删除操作效率低,修改和查找效率高。
3.2 链表 Linklist
链表又称单链表、链式存储结构,用于存储逻辑关系为“一对一”的数据。
和顺序表不同同,使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。
所以在链表中,每个数据元素可以配有一个指针用于找到下一个元素即节点,这意味着,链表上的每个“元素”都长下图这个样子:
3.2.1 链表的特性
逻辑结构:线性结构
存储结构:链式存储
特点:内存不连续,需要通过指针链接,大小不固定。解决顺序表插入删除麻烦的问题。
操作:增删改查
struct node
{
int data; //数据域:存储数据
struct node *next; //指针域:存储下一个节点的地址
};
3.2.2 单向链表
有头链表:存在一个头节点,头节点数据域无效,指针域有效。
无头链表:每一个节点的数据域和指针域都有效。
遍历无头单向链表
#include <stdio.h>
#include <stdlib.h>
typedef char datatype;
typedef struct node
{
datatype data; //数据域用来存数据
struct node *next; //指针域用来存下一个节点的地址
} node_t, *node_p;
// node_t A; //等同于 struct node A;
// node_p p; //等同于 struct node *p;
int main(int argc, char const *argv[])
{
//1.定义4个节点
node_t A = {'a', NULL};
node_t B = {'b', NULL};
node_t C = {'c', NULL};
node_t D = {'d', NULL};
//2.连接四个节点
A.next = &B; //连接A和B
B.next = &C;
C.next = &D;
//3.定义一个头指针指向第一个节点
node_p p = &A;
//4. 通过指针遍历无头单向链表
while (p != NULL)
{
printf("%c ", p->data); //打印节点中的数据域 a b c d
p = p->next; //让p指向下一个节点
}
printf("\n");
return 0;
}
遍历有头单向链表
#include <stdio.h>
#include <stdlib.h>
typedef char datatype;
typedef struct node
{
datatype data; //数据域用来存数据
struct node *next; //指针域用来存下一个节点的地址
} node_t, *node_p;
// node_t A; //等同于 struct node A;
// node_p p; //等同于 struct node *p;
int main(int argc, char const *argv[])
{
//1.定义4个节点
node_t A = {'a', NULL};
node_t B = {'b', NULL};
node_t C = {'c', NULL};
node_t D = {'d', NULL};
//2.连接四个节点
A.next = &B; //连接A和B
B.next = &C;
C.next = &D;
//3. 定义头节点,数据域无效,指针域有效
node_t H = {'\0', &A};
//4. 定义一个头指针,指向头结点
node_p p = &H;
#if 0
p = p->next; //先跨越头节点,指向第一个数据域有效的节点A
while (p != NULL) //相当于遍历无头链表
{
printf("%c ", p->data);
p = p->next;
}
printf("\n");
#else
while (p->next != NULL)
{
p = p->next;
printf("%c ", p->data);
}
printf("\n");
#endif
return 0;
}
链表尾插法练习
写一个有头单向链表,用于保存输入的学生成绩,实现一输入学生成绩就创建一个新的节点,将成绩保存起来。再将该节点链接到链表的尾,直到输入-1结束。
要求:每个链表的节点由动态内存分配得到 , 也就是用malloc。
过程:
- malloc申请空间link_node_t大小作为头节点
- 将新节点放到链表尾部
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
} node_t, *node_p;
int main(int argc, char const *argv[])
{
node_p pnew = NULL;
node_p ptail = NULL;
int score = -1;
//1. 创建一个头结点,用头指针p指向头节点
node_p p = (node_p)malloc(sizeof(node_t));
if (NULL == p)
{
perror("p malloc err");
return -1;
}
p->next = NULL; //初始化头节点
ptail = p; //让尾指针一开始指向头节点
//2. 循环输入学生成绩直到-1结束,创建新节点保存学生成绩,尾插到链表。
while (1)
{
scanf("%d", &score);
if (score == -1)
break;
//(1) 创建新节点用来保存学生成绩
pnew = (node_p)malloc(sizeof(node_t));
if (NULL == pnew)
{
perror("pnew malloc err");
return -1;
}
//(2) 初始化新节点,数据域保存学生成绩,指针域置空
pnew->data = score;
pnew->next = NULL;
//(3) 将新节点连接到链表尾部,也就是ptail所指节点的指针域等于新节点地址
ptail->next = pnew;
//(4) 将尾指针移动到新节点,因为尾指针ptail要一直指向最后一个节点
ptail = pnew;
}
//3. 遍历有头链表
while (p->next != NULL)
{
p = p->next;
printf("%d ", p->data);
}
printf("\n");
return 0;
}
有头单向链表的函数操作
插入指定位置节点:
删除指定节点:
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
} node_t, *node_p;
//创建一个空的有头单向链表
node_p createEmptyLinkList() //create创建 Empty空的 LinkList单向链表
{
//开辟链表节点大小空间
node_p p = (node_p)malloc(sizeof(node_t));
if (NULL == p)
{
perror("createEmptyLinkList");
return NULL;
}
//初始化头节点
p->next = NULL;
return p;
}
//计算链表的长度。
int lengthLinkList(node_p p)
{
int len = 0;
while (p->next != NULL)
{
p = p->next;
len++;
}
return len;
}
//向单向链表的指定位置插入数据
//p保存链表的头指针 post 插入的位置 data插入的数据
int insertIntoPostLinkList(node_p p, int post, datatype data)
{
//容错判断
if (post < 0 || post > lengthLinkList(p))
{
printf("insertIntoPostLinkList err\n");
return -1;
}
node_p pnew = NULL; //记录新节点
// 将指针 遍历到插入位置的前一个节点
for (int i = 0; i < post; i++)
p = p->next;
// malloc创建一个新节点, 初始化新节点
pnew = (node_p)malloc(sizeof(node_t));
if (NULL == pnew)
{
perror("insertIntoPostLinkList err");
return -1;
}
pnew->data = data;
pnew->next = NULL;
// 连接新节点到链表, 先连后面,再连前面。(不然前面断了后面就找不到了)
pnew->next = p->next;
p->next = pnew;
return 0;
}
//遍历单向链表
void showLinkList(node_p p)
{
while (p->next != NULL)
{
p = p->next;
printf("%d ", p->data);
}
printf("\n");
}
int isEmptyLinkList(node_p p)
{
}
//删除单向链表中指定位置的数据 post 代表的是删除的位置
int deletePostLinkList(node_p p, int post)
{
}
int main(int argc, char const *argv[])
{
node_p p = createEmptyLinkList();
for (int i = 0; i < 5; i++)
insertIntoPostLinkList(p, i, i);
showLinkList(p);
insertIntoPostLinkList(p, -1, 10); //insertIntoPostLinkList err 容错判断打印结果
return 0;
}