《数据结构》数据结构概念,顺序表,链表

目录

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. 逻辑结构

概念:描述数据之间的逻辑规律和联系,即元素与元素之间的关系

逻辑结构的分类:

  1. 线性结构(头节点无前驱,尾节点无后缀)
    1. 线性存储
    2. 一对一的关系
    3. 顺序表、链表
  1. 层次结构(根节点无前驱,叶子节点无后继)
    1. 一对多的关系
  1. 网状结构
    1. 多对多的关系

2.3. 存储结构

概念:数据的逻辑结构在计算机中的具体实现

  1. 存储结构分类
    1. 顺序存储:内存空间开辟是连续(数组:内存空间连续开辟,数据元素类型相同)
    2. 链式存储:通过地址数据元素联系在一起
    3. 索引存储:通过索引表找到数据元素存放位置,拿到数据
    4. 散列存储结构 (哈希存储)
      1. 数据元素的存放和位置之间存在一个关系。
      2. 存在一个关键字key和一个关系函数,通过关键值key带入关系函数计算出数据存放的位置。对应位置存放、对应位置取值。

2.4. 操作

操作:增 删 改 查

3. 算法

3.1. 算法与程序

算法:解决问题的思想办法

程序:用计算机语言对算法的具体实现

3.2. 算法与数据结构

算法+数据结构=程序

算法的设计:依赖于逻辑结构

算法的实现:依赖于存储结构

3.3. 算法的特性

  1. 有穷性:算法的执行步骤是有限的
  2. 确定性:算法的每一个步骤,无二义性 ,没有歧义
  3. 可行性:算法能够在有限的时间内完成
  4. 输入和输出:一个算法可以有一个或多个输入和输出

3.4. 如何评价一个算法的好坏

  1. 正确性:保证算法能够正确的完成功能的实现
  2. 易读性:算法容易被解读
  3. 健壮性:错误处理
  4. 高效性:算法执行效率,算法执行快慢容易受到计算机性能的
  5. 低存储性:算法占用空间小,空间复杂度

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;
}

顺序表和单向链表比较

顺序表

链表

空间

空间连续

通过指针链接

长度

固定

不固定

特点

查找方便,但是插入和删除麻烦

插入方便,查找麻烦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值