Featured image of post 计算机基础 数据结构与算法 顺序表

计算机基础 数据结构与算法 顺序表

🌏排序 数据结构与算法实践 🎯 这篇文章用于记录有关 顺序表的动态数组实现(C语言 || Java语言)

🎄顺序表C实现

🔮头文件结构声明

//
// Created by Liu Xianmeng on 2023/9/30 23:00
//
// 基于数组的线性表的实现 头文件
//
#include <stdio.h>
#include <stdlib.h>
#ifndef CDATASTRCUTRUES_20230930_SEQUENCELIST_H
#define CDATASTRCUTRUES_20230930_SEQUENCELIST_H

#define INIT_SIZE 100 // 线性表存储空间的初始化分配
#define INCREMENT 50  // 线性表存储空间的增量分配

// 静态分配方式(❌实际顺序表的实现不使用静态内存分配)
typedef struct {
    // 一次性分配存储空间
    int data[INIT_SIZE]; // 存储空间 取数组
    int size; // 线性表的实际存储元素的数目
    int capacity; // 线性表的容量(实际声明的内存容量)
}StaticSequenceList; // 静态分配方式

// 动态分配方式(✅其他的函数都将使用动态内存分配的方式来进行)
typedef struct {
    int *data;
    int size; // 实际存储大小
    int capacity; // 声明的空间大小
}DynamicSequenceList; // 动态分配方式

// 打印绿色的success信息
void printf_green(const char *s){
    printf("\033[0m\033[1;32m%s\033[0m", s);
}

/**
 * 初始化线性表
 * @param L   传入一个空新创建的动态线性表
 * @return    初始化成功返回 1 失败返回 0
 */
int initList(DynamicSequenceList *L) {
    ...
}

/**
 * 向指定位置插入一个元素
 * @param L         要操作的顺序表对象
 * @param pos       指定插入数据的下标位置(注意是下标位置)
 * @param elem      要插入的元素
 * @return			1表示插入成功 0表示插入失败
 */
int insertOne(DynamicSequenceList *L, int pos, int elem) {
    ...
}

/**
 * 删除指定的pos位置的元素 用deletedElem指针接收被删除元素的值
 * @param L
 * @param pos
 * @param deletedElem
 * @return 1表示删除成功 0表示删除失败
 */
int deleteOne(DynamicSequenceList *L, int pos, int *deletedElem) {
    ...
}

#endif //CDATASTRCUTRUES_20230930_SEQUENCELIST_H

🔮初始化&&插入数据

/**
 * 初始化线性表
 * @param L   传入一个空新创建的动态线性表
 * @return      初始化成功返回 1 失败返回 0
 */
int initList(DynamicSequenceList *L) {
    // 声明一个不包含实际数据的存储区域
    L->data = (int*) malloc(INIT_SIZE * sizeof(int));
    L->size = 0; // 初始化的时候存储的元素个数为0
    L->capacity = INIT_SIZE; // 初始化的时候存储容量为INIT_SIZE
    if(!L->data) {
        perror("S DynamicSequenceList M initList() -> 分配失败 程序退出\n");
        return -1; // 如果分配失败 则直接返回-1 程序结束
    }
    printf_green("S DynamicSequenceList M initList() -> 分配成功 程序退出\n");
    return 1; // 分配成功返回
}

/**
 * 向指定位置插入一个元素
 * @param L         要操作的顺序表对象
 * @param pos       指定插入数据的下标位置(注意是下标位置)
 * @param elem      要插入的元素
 * @return
 */
int insertOne(DynamicSequenceList *L, int pos, int elem) {
    // pos最小是0(当线性表的size本身就等于0的情况)
    // pos最大是size(插入到线性表的尾部)
    if(pos < 0 || pos > L->size) {
        perror("传入的下标不合法");
    }

    /***** 其他位置则合法 这个时候又需要判断一个东西 那就是L的容量是否已经满了 *****/

    // 如果L的capacity===size则说明容量已经满了 需要进行扩容
    if(L->size == L->capacity) {
        // 使用realloc函数进行扩容 第1个参数为原来的数据指针 第2个参数为扩容后的大小
        int *p = (int *) realloc(L->data, (L->size + INCREMENT)*sizeof(int));
        if(!p) {
            perror("S DynamicSequenceList M insertOne() -> 扩容失败");
            return -1;
        } else {
            // 扩容成功
            L->data = p;
            // 更新容量
            L->capacity = L->size + INCREMENT;
        }
    }

    // 执行插入操作 需要把后面的 L.size - pos 个元素向后移动一位
    int count = 1;
    while(count <= L->size - pos) {
        *(L->data + L->size - count + 1) = *(L->data + L->size - count);
        ++count;
    }
    // 移动结束之后 把elem插入到当前的位置
    *(L->data + pos) = elem;
    printf("在%d位置插入数字%d\n", pos, elem);
    ++L->size;
    return -1;
}

⚡测试初始化&&插入数据

//
// Created by Liu Xianmeng on 2023/9/30 23:44
//
#include <stdio.h>
#include "SequenceList.h" // ⚡⚡⚡包含顺序表的头文件

int main() {
    printf_green("main() -> program start..\n");
    DynamicSequenceList L;
    initList(&L);
    insertOne(&L, 0, 1);
    insertOne(&L, 0, 2);
    insertOne(&L, 0, 3);
    insertOne(&L, 3, 4);
    int *p = L.data;
    int count = 1;
    while(count != L.size + 1) {
        printf("遍历DynamicSequenceList -> *p = %d\n", *p);
        ++p; // 指向下一个元素所在的位置
        ++count;
    }
    return 0;
}

🔮删除指定元素

/**
 * 删除指定的pos位置的元素 并返回已经删除的元素
 * @param L
 * @param pos
 * @param deletedElem
 * @return 1表示删除成功 0表示删除失败
 */
int deleteOne(DynamicSequenceList *L, int pos, int *deletedElem) {
    // 1 判断删除pos的边界
    if(pos < 0 || pos >= L->size) {
        //perror("S DynamicSequenceList M deleteOne() -> pos超出边界");
        return 0; // 删除失败
    }

    // 2 执行删除操作
    *deletedElem = *(L->data + pos); // deletedElem指针指向被删除的数
    for(int i = pos + 1; i <= L->size - 1; ++i) { 
        // [pos + 1, L->size - 1] 范围的元素向前移动一位
        *(L->data + i - 1) = *(L->data + i);
    }
    --L->size; // size减1

    // 3 删除成功 返回
    return 1;
}

⚡执行测试

//
// Created by Liu Xianmeng on 2023/9/30 23:44
//
#include <stdio.h>
#include "SequenceList.h"

int main() {
    printf_green("main() -> program start..\n");
    DynamicSequenceList L;
    initList(&L);
    insertOne(&L, 0, 1);
    insertOne(&L, 0, 2);
    insertOne(&L, 0, 3);

    int e;
    int rst = deleteOne(&L, 0, &e);
    if(rst == 1) printf("deleteOne(&L, 0, e) -> 被删除的元素是%d\n", e);
    else printf("deleteOne(&L, 0, e) -> 下标越界 删除失败\n");

    int rst2 = insertOne(&L, 3, 4);
    if(rst2 == 1) printf("insertOne(&L, 3, 4) -> 被插入的元素是%d\n", e);
    else printf("insertOne(&L, 3, 4) -> 下标越界 插入失败\n");

    int *p = L.data;
    int count = 1;
    while(count != L.size + 1) {
        printf("遍历DynamicSequenceList -> *p = %d\n", *p);
        ++p;
        ++count;
    }
    return 0;
}

测试结果

🔮合并两个顺序表

/**
 * 假设L1和L2是非递减的顺序 这个函数将L1和L2合并到L3
 * @param L1
 * @param L2
 * @param L3
 */
void mergeTwoList(DynamicSequenceList L1, DynamicSequenceList L2, DynamicSequenceList *L3) {
    int len1 = L1.size;
    int len2 = L2.size;
    int i = 0, j = 0, k = 0;
    while(i < len1 && j < len2) {
        if(*(L1.data + i) <= *(L2.data + j)) {
            *(L3->data + k++) = *(L1.data + i++);
        } else {
            *(L3->data + k++) = *(L2.data + j++);
        }
        ++L3->size; // 注意要更新L3的size大小!
    }
    while(i < len1) {
        *(L3->data + k++) = *(L1.data + i++);
        ++L3->size; // 注意要更新L3的size大小!
    }
    while(j < len2) {
        *(L3->data + k++) = *(L2.data + j++);
        ++L3->size; // 注意要更新L3的size大小!
    }
    ////////////////// 合并完成  //////////////////
}

⚡执行测试

//
// Created by Liu Xianmeng on 2023/9/30 23:44
//
#include <stdio.h>
#include "SequenceList.h"

int main() {
    printf_green("main() -> program start..\n");
    DynamicSequenceList L1;
    initList(&L1);
    insertOne(&L1, 0, 1);
    insertOne(&L1, 1, 2);
    insertOne(&L1, 2, 3);
    DynamicSequenceList L2;
    initList(&L2);
    insertOne(&L2, 0, 1);
    insertOne(&L2, 1, 2);
    insertOne(&L2, 2, 3);
    DynamicSequenceList L3;
    initList(&L3);
    mergeTwoList(L1, L2, &L3);
    int lenL3 = L3.size;
    int count = 0;
    printf("合并后的顺序表 = [");
    while(count < lenL3) {
        // 不换行遍历打印
        printf("%d,", *(L3.data + count++));
    }
    printf("]");
    return 0;
}

测试结果

🎄顺序表java实现

package bigbigmeng.datastructures.array;

/**
@author Liu Xianmeng
@createTime 2023/5/1 9:18
@instruction 实现动态数组
*/

@SuppressWarnings({"all"})
public class DynamicArray {
    private int size = 0; // 数组的实际大小
    private int capacity = 8; // 数组的容量
    private int[] array = {}; // 数组本身

    /**
     * 检查扩容 如果当前容量不够 则进行扩容
     */
    private void checkAndGrow() {
        // 如果实际容量已满 则进行下面的扩容判断
        if(size == capacity) {
            // 扩容为原来的1.5倍
            capacity += capacity >> 1;
            int[] newArray = new int[capacity];
            // 把原数组的内容从下标0开始拷贝size个元素到newArray
            System.arraycopy(array, 0, newArray, 0, size);
            array = newArray;
            return;
        }

        // 如果实际容量为0 则初始化数组
        if(size == 0) {
            array = new int[capacity];
            return;
        }

        // 如果实际容量未满 则什么也不做
    }

    /**
     * 如果不指定插入位置 则在数组的最后追加元素
     */
    public void addLast(int e) {
        add(size, e);
    }

    /**
     * 向 [0 .. size] 位置添加元素
     */
    public void add(int index, int e) {
        // 扩容检查
        checkAndGrow();
        // 下标要合法
        if(index >= 0 && index < size) {
            System.arraycopy(array, index, array, index + 1, size - index);
        } else {
            throw new IndexOutOfBoundsException("下标越界/错误");
        }
        array[index] = e;
        ++size;
    }

    /**
     * 从 [0 .. size] 位置删除元素
     */
    public int remove(int index) {
        if(index >= 0 && index < size) {
            int removed = array[index];
            System.arraycopy(array, index + 1, array, index, size - index - 1);
            --size;
            return removed;
        }
        throw new IndexOutOfBoundsException("下标错误");
    }

    /**
     * 获取 [0 .. size] 位置元素
     */
    public int get(int index) {
        return array[index];
    }
}

🎄总结

C的实现涉及到内存的实际操作 而Java的实现封装了内存的操作 因为使用Java实现动态顺序表会相对简单 但是使用C可以更好地体会程序对内存的实际掌控