目录
1. 为什么学习数据结构?
2. 数据结构
2.1. 数据
2.2. 逻辑结构
2.3. 存储结构
2.4. 操作
3. 算法
3.1. 算法与程序
3.2. 算法与数据结构
3.3. 算法的特性
3.4. 如何评价一个算法的好坏
4. 线性表
4.1. 顺序表
4.2. 单向链表
4.3. 单向循环链表(解决约瑟夫问题)
1. 为什么学习数据结构?
C语言:学习的时如何写代码
数据结构:教会我们高效简洁的写代码。
实现一个功能、写代码解决什么问题?
1》数据与数据之间的逻辑规律和数据在计算机中如何表示(存储)
数据结构:数据的逻辑结构存储结构及操作
数据:不只是一个单独的数值、是一个集合的概念。
2》解决问题的方法(实现代码的逻辑思想)
算法
数据结构+算法=程序
2. 数据结构
概念: 数据的逻辑结构存储结构及操作。
2.1. 数据
数据:不只是一个单独的数值,是一个集合的概念。
数据元素:数据的最小单位,由基本的数据项构成。
数据项:是数据元素的基本单位,描述数据元素拥有的信息。
节点:就是数据元素
2.2. 逻辑结构
概念:描述数据之间的逻辑规律和联系,即元素与元素之间的关系
逻辑结构的分类:
- 线性结构(头节点无前驱,尾节点无后缀)
-
- 线性存储
- 一对一的关系
- 顺序表、链表
- 层次结构(根节点无前驱,叶子节点无后继)
-
- 一对多的关系
- 网状结构
-
- 多对多的关系
- 图
2.3. 存储结构
概念:数据的逻辑结构在计算机中的具体实现
- 存储结构分类
-
- 顺序存储:内存空间开辟是连续(数组:内存空间连续开辟,数据元素类型相同)
- 链式存储:通过地址将数据元素联系在一起
- 索引存储:通过索引表找到数据元素存放位置,拿到数据
- 散列存储结构 (哈希存储)
-
-
- 数据元素的存放和位置之间存在一个关系。
- 存在一个关键字key和一个关系函数,通过关键值key带入关系函数计算出数据存放的位置。对应位置存放、对应位置取值。
-
2.4. 操作
操作:增 删 改 查
3. 算法
3.1. 算法与程序
算法:解决问题的思想办法
程序:用计算机语言对算法的具体实现
3.2. 算法与数据结构
算法+数据结构=程序
算法的设计:依赖于逻辑结构
算法的实现:依赖于存储结构
3.3. 算法的特性
- 有穷性:算法的执行步骤是有限的
- 确定性:算法的每一个步骤,无二义性 ,没有歧义
- 可行性:算法能够在有限的时间内完成
- 输入和输出:一个算法可以有一个或多个输入和输出
3.4. 如何评价一个算法的好坏
- 正确性:保证算法能够正确的完成功能的实现
- 易读性:算法容易被解读
- 健壮性:错误处理
- 高效性:算法执行效率,算法执行快慢容易受到计算机性能的
- 低存储性:算法占用空间小,空间复杂度
4. 线性表
- 线性表:
-
- 顺序表
- 链表(单向链表 单向循环链表 双向链表 双向循环链表)
- 栈
- 队列
- 特点:一对一的关系,头节点没有前驱,尾节点没有后继
4.1. 顺序表
- 特点:内存空间是连续开辟(数组)
- 逻辑结构:线性结构
- 存储结构:顺序存储结构
- 定义一个结构体表示顺序表
#define N 10
typedef int datatype_t;
typedef struct list_t
{
datatype_t data[N];//表
int last ; //保存最后一个有效元素的下标 (-1 表为空)
}seqlist_t,*seqlist_pj;
- 常见操作
#include <stdio.h>
#include <stdlib.h>
#define N 10
typedef int datatype_t;
typedef struct list_t {
datatype_t data[N]; // 表
int last; // 保存最后一个有效元素的下标 (-1 表为空)
} seqlist_t, *seqlist_p;
// 创建一个空的顺序表
seqlist_p createList() {
seqlist_p list = (seqlist_p)malloc(sizeof(seqlist_t));
list->last = -1;
return list;
}
// 向顺序表的指定位置插入数据
void insert(seqlist_p list, int pos, int value) {
if (pos < 0 || pos > list->last + 1 || list->last == N - 1) {
printf("插入位置无效或顺序表已满\n");
return;
}
// 将插入位置之后的元素依次后移
for (int i = list->last; i >= pos; i--) {
list->data[i+1] = list->data[i];
}
// 在指定位置插入新元素
list->data[pos] = value;
list->last++;
}
// 判断顺序表是否满
int isFull(seqlist_p list) {
return list->last == N - 1;
}
// 指定位置删除数据
void delete(seqlist_p list, int pos) {
if (pos < 0 || pos > list->last) {
printf("删除位置无效\n");
return;
}
// 将删除位置之后的元素依次前移
for (int i = pos; i < list->last; i++) {
list->data[i] = list->data[i+1];
}
list->last--;
}
// 判断顺序表是否为空
int isEmpty(seqlist_p list) {
return list->last == -1;
}
// 修改指定位置的数据
void modify(seqlist_p list, int pos, int value) {
if (pos < 0 || pos > list->last) {
printf("修改位置无效\n");
return;
}
list->data[pos] = value;
}
// 删除指定的数据
void removeData(seqlist_p list, int value) {
for (int i = 0; i <= list->last; i++) {
if (list->data[i] == value) {
// 将删除位置之后的元素依次前移
for (int j = i; j < list->last; j++) {
list->data[j] = list->data[j+1];
}
list->last--;
i--; // i减1以继续判断下一个元素是否与value相等
}
}
}
// 清空顺序表
void clearList(seqlist_p list) {
list->last = -1;
}
// 删除顺序表
void deleteList(seqlist_p list) {
free(list);
}
// 遍历顺序表
void traverse(seqlist_p list) {
if (isEmpty(list)) {
printf("顺序表为空\n");
return;
}
printf("顺序表数据:");
for (int i = 0; i <= list->last; i++) {
printf("%d ", list->data[i]);
}
printf("\n");
}
int main() {
seqlist_p list = createList();
insert(list, 0, 1);
insert(list, 1, 2);
insert(list, 2, 3);
traverse(list);
delete(list, 1);
traverse(list);
modify(list, 0, 5);
traverse(list);
removeData(list, 5);
traverse(list);
clearList(list);
traverse(list);
deleteList(list);
return 0;
}
- 顺序表的特点
1.内存空间连续开辟
2.长度固定(保存的数据元素个数是固定的) #define N 10
3.插入和删除比较复杂,查询操作或修改比较简单。
4.2. 单向链表
- 特点:内存空间开辟不是连续、通过地址将多有的内存空间练习到一起
- 逻辑结构:线性结构
- 存储结构:链式存储
- 分类:
-
- 有头单向链表
-
-
- 链表中的头节点数据域无效,指针域有效
-
-
- 无头单向链表
-
-
- 链表中所有节点的数据域和指针域都是有效的
-
- 定义链表节点结构体:
typedef int datatype_t;
typedef struct node_t //node 节点
{
datatype_t data; //数据域
struct node_t *next; //指针域 保存下一个节点的地址
}linklist_t,*linklist_p; //link 链
- 有头单向链表
#include <stdio.h>
#include <stdlib.h>
typedef int datatype_t;
typedef struct node_t
{
datatype_t data; //数据域
struct node_t *next; //节点域
}linklist_t, *linklist_p;
// 创建空的有头单向链表
void createEmptyList(linklist_p *head)
{
*head = (linklist_p)malloc(sizeof(linklist_t));
(*head)->next = NULL;
}
// 向链表的指定位置插入数据
void insertData(linklist_p head, int position, datatype_t data)
{
linklist_p p = head;
int count = 0;
while (p && count < position)
{
p = p->next;
count++;
}
if (p && count == position)
{
linklist_p newNode = (linklist_p)malloc(sizeof(linklist_t));
newNode->data = data;
newNode->next = p->next;
p->next = newNode;
}
}
// 计算链表的长度
int calculateLength(linklist_p head)
{
linklist_p p = head->next;
int length = 0;
while (p)
{
length++;
p = p->next;
}
return length;
}
// 遍历链表
void traverseList(linklist_p head)
{
linklist_p p = head->next;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
// 删除链表指定位置的节点
void deleteNode(linklist_p head, int position)
{
linklist_p p = head;
int count = 0;
while (p && count < position)
{
p = p->next;
count++;
}
if (p && p->next && count == position)
{
linklist_p temp = p->next;
p->next = p->next->next;
free(temp);
}
}
// 修改链表指定位置的数据
void modifyData(linklist_p head, int position, datatype_t data)
{
linklist_p p = head->next;
int count = 0;
while (p && count < position)
{
p = p->next;
count++;
}
if (p && count == position)
{
p->data = data;
}
}
// 查询指定数据的位置
int findPosition(linklist_p head, datatype_t data)
{
linklist_p p = head->next;
int position = 0;
while (p)
{
if (p->data == data)
{
return position;
}
p = p->next;
position++;
}
return -1;
}
// 删除指定的数据
void deleteData(linklist_p head, datatype_t data)
{
linklist_p p = head;
while (p->next)
{
if (p->next->data == data)
{
linklist_p temp = p->next;
p->next = p->next->next;
free(temp);
}
else
{
p = p->next;
}
}
}
// 清空链表
void clearList(linklist_p head)
{
linklist_p p = head->next;
while (p)
{
linklist_p temp = p;
p = p->next;
free(temp);
}
head->next = NULL;
}
// 链表的倒置
void reverseList(linklist_p *head)
{
linklist_p prev = NULL;
linklist_p current = *head;
linklist_p next = NULL;
while (current != NULL)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head = prev;
}
// 判断链表是否为空
int isEmpty(linklist_p head)
{
return head->next == NULL;
}
int main()
{
linklist_p head;
createEmptyList(&head);
insertData(head, 0, 1);
insertData(head, 1, 2);
insertData(head, 2, 3);
insertData(head, 3, 4);
insertData(head, 4, 5);
printf("List: ");
traverseList(head);
printf("Length: %d\n", calculateLength(head));
deleteNode(head, 2);
printf("After deleting node at position 2: ");
traverseList(head);
modifyData(head, 1, 10);
printf("After modifying data at position 1: ");
traverseList(head);
int position = findPosition(head, 5);
printf("Position of data 5: %d\n", position);
deleteData(head, 1);
printf("After deleting data 1: ");
traverseList(head);
clearList(head);
printf("After clearing the list: ");
traverseList(head);
insertData(head, 0, 1);
insertData(head, 1, 2);
insertData(head, 2, 3);
insertData(head, 3, 4);
insertData(head, 4, 5);
printf("List: ");
traverseList(head);
reverseList(&head);
printf("After reversing the list: ");
traverseList(head);
printf("Is list empty? %s\n", isEmpty(head) ? "Yes" : "No");
return 0;
}
- 无头单向链表
#include <stdio.h>
#include <stdlib.h>
// typedef定义的数据类型
typedef int datatype_t;
typedef struct node_t {
datatype_t data;
struct node_t* next;
} linklist_t, * linklist_p;
// 创建空的有头单向链表
void createEmptyList(linklist_p* head) {
*head = NULL;
}
// 向链表的指定位置插入数据
int insertElement(linklist_p* head, int position, datatype_t value) {
// 创建新节点
linklist_p new_node = (linklist_p)malloc(sizeof(linklist_t));
if (new_node == NULL) {
return 0; // 内存分配失败
}
new_node->data = value;
new_node->next = NULL;
if (position == 0) {
new_node->next = *head;
*head = new_node;
}
else {
int count = 0;
linklist_p current = *head;
while (current != NULL && count < position - 1) {
current = current->next;
count++;
}
if (current == NULL) {
return 0; // 指定位置无效
}
new_node->next = current->next;
current->next = new_node;
}
return 1;
}
// 计算链表的长度
int getLength(linklist_p head) {
int length = 0;
linklist_p current = head;
while (current != NULL) {
length++;
current = current->next;
}
return length;
}
// 遍历链表
void traverse(linklist_p head) {
linklist_p current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
// 删除链表指定位置的数据
int deleteElement(linklist_p* head, int position) {
if (*head == NULL) {
return 0; // 链表为空
}
if (position == 0) {
linklist_p temp = *head;
*head = (*head)->next;
free(temp);
}
else {
int count = 0;
linklist_p current = *head;
linklist_p previous = NULL;
while (current != NULL && count < position) {
previous = current;
current = current->next;
count++;
}
if (current == NULL) {
return 0; // 指定位置无效
}
previous->next = current->next;
free(current);
}
return 1;
}
// 修改链表指定位置的数据
int modifyElement(linklist_p head, int position, datatype_t value) {
int count = 0;
linklist_p current = head;
while (current != NULL && count < position) {
current = current->next;
count++;
}
if (current == NULL) {
return 0; // 指定位置无效
}
current->data = value;
return 1;
}
// 查询指定数据的位置
int findPosition(linklist_p head, datatype_t value) {
int position = 0;
linklist_p current = head;
while (current != NULL) {
if (current->data == value) {
return position;
}
current = current->next;
position++;
}
return -1; // 没有找到指定数据
}
// 删除指定的数据
int deleteValue(linklist_p* head, datatype_t value) {
if (*head == NULL) {
return 0; // 链表为空
}
if ((*head)->data == value) {
linklist_p temp = *head;
*head = (*head)->next;
free(temp);
return 1;
}
linklist_p current = *head;
linklist_p previous = NULL;
while (current != NULL && current->data != value) {
previous = current;
current = current->next;
}
if (current == NULL) {
return 0; // 没有找到指定数据
}
previous->next = current->next;
free(current);
return 1;
}
// 清空链表
void clearList(linklist_p* head) {
linklist_p current = *head;
while (current != NULL) {
linklist_p temp = current;
current = current->next;
free(temp);
}
*head = NULL;
}
// 链表的倒置
void reverseList(linklist_p* head) {
linklist_p previous = NULL;
linklist_p current = *head;
linklist_p next = NULL;
while (current != NULL) {
next = current->next;
current->next = previous;
previous = current;
current = next;
}
*head = previous;
}
// 判断链表是否为空
int isListEmpty(linklist_p head) {
return head == NULL;
}
int main() {
linklist_p linked_list;
createEmptyList(&linked_list);
insertElement(&linked_list, 0, 1);
insertElement(&linked_list, 1, 2);
insertElement(&linked_list, 2, 3);
insertElement(&linked_list, 3, 4);
traverse(linked_list); // 输出: 1 2 3 4
printf("Length: %d\n", getLength(linked_list)); // 输出: 4
deleteElement(&linked_list, 2);
traverse(linked_list); // 输出: 1 2 4
modifyElement(linked_list, 1, 5);
traverse(linked_list); // 输出: 1 5 4
int position = findPosition(linked_list, 5);
printf("Position: %d\n", position); // 输出: 1
deleteValue(&linked_list, 1);
traverse(linked_list); // 输出: 5 4
clearList(&linked_list);
printf("Is empty: %s\n", isListEmpty(linked_list) ? "Yes" : "No"); // 输出: Yes
insertElement(&linked_list, 0, 1);
insertElement(&linked_list, 1, 2);
insertElement(&linked_list, 2, 3);
insertElement(&linked_list, 3, 4);
traverse(linked_list); // 输出: 1 2 3 4
reverseList(&linked_list);
traverse(linked_list); // 输出: 4 3 2 1
return 0;
}
- 链表的特点
1.内存空间不连续,通过地址将地址空间联系在一起
2.长度不固定
3.删除和插入简单,查询和修改复杂
4.3. 单向循环链表(解决约瑟夫问题)
- 特点:无头单向链表尾节点的指针域保存头节点的地址, 就可以形成单向循环链表
- 通过一定规律找到一个最终的值
- 定义链表结构体
typedef struct node_t
{
int data;
struct node_t *next;
}link_t;
- 单向循环链表
#include <stdio.h>
#include <stdlib.h>
#define N 10
typedef struct node_t
{
int data;
struct node_t *next;
} link_t;
int main()
{
// 1.创建一个单向无头链表
link_t *tail = NULL;
link_t *p = (link_t *)malloc(sizeof(link_t));
if (p == NULL)
{
puts("mallod error");
return -1;
}
p->data = 1;
p->next = NULL;
tail = p; // 指针tail指向了p
// 创建接下来的节点
for (int i = 0; i < N; i++)
{
tail->next = (link_t *)malloc(sizeof(link_t));
if (tail->next == NULL)
{
puts("mallod error");
return -1;
}
tail = tail->next;
tail->data = i + 1;
tail->next = NULL;
}
tail->next = p; // 尾节点指向头节点
// 解决约瑟夫问题
int start_num = 3;
int n = 3;
link_t *pdel = NULL;
// 1.将头指针移动到K
for (int i = 0; i < start_num - 1; i++)
{
p = p->next;
}
pdel = p->next;
while (p != p->next)
{
for (int i = 0; i < n - 1; i++)
p = p->next;
pdel = p->next;
p->next = pdel->next;
free(pdel);
pdel = NULL;
}
printf("king is %d\n", p->data);
return 0;
}
顺序表和单向链表比较
顺序表 | 链表 | |
空间 | 空间连续 | 通过指针链接 |
长度 | 固定 | 不固定 |
特点 | 查找方便,但是插入和删除麻烦 | 插入方便,查找麻烦 |