本代码编辑在CLion环境上,若要复制代码运行起来需要修改一些地方。
代码分为 3 个部分,头文件 list.h, 实现头文件的 list.c 和 main.c 文件。
大部分代码都有注释,但是没有配合对应的图解,需要在读代码是自己构想。
代码纯手写,如有不足之处,请多指教
main.c 部分
#include <stdio.h>
#include "list.h"
int main() {
struct list* list = list_init();
list_add(list, 99);
list_print(list);
list_add(list, 78);
list_insert(list, 0, 88);
list_print(list);
list_add(list, 100);
list_print(list);
int e;
list_remove(list, 1, &e);
list_print(list);
printf("%d %d\n", list_get(list, 1), list_get(list, 2));
list_set(list, 0, 5);
list_print(list);
list_free(list);
return 0;
}
list.h 部分
//
// Created by Sur qing on 2022/5/17.
//
#ifndef SEQUENCE_LIST_H
#define SEQUENCE_LIST_H
struct list;
/*
功能:在堆里创建一个空的线性表,成功返回有效指针,失败返回NULL。
前提条件:无。
*/
struct list* list_init();
/*
功能:释放线性表list
前提条件:list必须是有效指针
*/
void list_free(struct list* list);
/*
功能:返回线性表list的数据元素个数
前提条件:list必须是有效指针
后置条件:数据元素个数必须 >= 0
*/
int list_count(struct list* list);
/*
功能:清空线性表list中的数据元素
前提条件:list必须是有效指针
后置条件:数据元素个数必须==0
*/
void list_clear(struct list* list);
/*
功能:判断线性表list是否为空,为空返回 1,否则返回 0
前提条件:list必须是有效指针
后置条件:无
*/
int list_isEmpty(struct list* list);
/*
功能:在线性表list查找元素e,找到返回 1,否则返回 0
前提条件:list必须是有效指针
后置条件:无
*/
int list_contains(struct list* list, int e);
/*
功能:在线性表list的index位置插入元素e,成功返回 1 ,失败返回 0
前提条件:1. list必须是有效指针
2. index>=0 && index <= count
后置条件:数据元素的个数count+1
*/
int list_insert(struct list* list, int index, int e);
/*
功能:在线性表list的末尾插入元素e
前提条件:list必须是有效指针
后置条件:数据元素的个数count+1
*/
void list_add(struct list* list, int e);
/*
功能:在线性表list的index位置写入元素e
前提条件:1. list必须是有效指针
2. index>=0 && index < count
后置条件:list[index] == e
*/
int list_set(struct list* list, int index, int e);
/*
功能:删除线性表list的index位置,被删除的元素写入参数e
前提条件:1. list必须是有效指针
2. index>=0 && index < count
后置条件:count-1
*/
void list_remove(struct list* list, int index, int* e);
/*
功能:返回线性表list的index位置的元素
前提条件:1. list必须是有效指针
2. index>=0 && index < count
后置条件:
*/
int list_get(struct list* list, int index);
/*
功能:打印出list中所有的元素
前提条件:1. list必须是有效指针
后置条件:
*/
void list_print(struct list* list);
#endif //SEQUENCE_LIST_H
list.c 部分
//
// Created by Sur qing on 2022/5/17.
//
#include <assert.h>
#include "stdlib.h"
#include "stdio.h"
#include "list.h"
#define INIT_SIZE 50
#define INCR_SIZE 50
// 定义 list 结构体
struct list {
int *data;
int size;
int count;
};
struct list* list_init() {
// 为 list 结构体分配内存
struct list* list = NULL;
list = (struct list*) malloc(sizeof(struct list));
assert(list != NULL);
/*
* 通过 assert 可以更方便我们 Debug,
* malloc, calloc, realloc 三个函数在执行时,在某些情况下会申请内存失败
* 而返回一个 NULL 指针,在这里通过 对 list 不为空的断言可以确保我们
* 后续的正常操作不会出现错误,同理后面类似
* */
// 将 list 结构体初始化
list->data = NULL;
list->data = (int*) calloc(INIT_SIZE, sizeof(int));
assert(list->data);
/*
* 这里还有存在一个细节问题,不过不能理解也没关系。
* 如果此时 list->data 的内存申请失败,断言会直接结束程序
* 那会出现一个问题,就是之前 list = (struct list*) malloc(sizeof(struct list));
* 申请的内存不会被释放,如何解决?
* 在断言之前应该在加一句判断:如果 list->data == NULL,
* 那么先将之前分配的内存先释放后在,再结束程序
* 同理在后面的代码中某些位置也会有类似的问题,就不在叙述了
* */
list->size = INIT_SIZE;
list->count = 0;
return list;
}
void list_free(struct list* list) {
assert(list);
// 释放指向数据的内存
free(list->data);
// 释放指向结构体的内存
free(list);
return;
}
int list_count(struct list* list) {
assert(list);
assert(list->count >= 0);
return list->count;
}
void list_clear(struct list* list) {
assert(list);
list->count = 0;
return;
}
int list_isEmpty(struct list* list) {
assert(list);
return list->count == 0;
}
int list_contains(struct list* list, int e) {
assert(list);
int i;
for (i = 0; i < list->count; i++) {
if (list->data[i] == e) return 1;
}
return 0;
}
int list_insert(struct list* list, int index, int e) {
assert(list);
// 如果 下标不合法,返回 0 表示插入失败
if (index < 0 || index > list->count) return 0;
// 如果链表已经存满,则需要增加内存
if (list->count >= list->size) {
int* temp = NULL;
temp = (int*) realloc(list->data, sizeof(int) * (list->size + INCR_SIZE));
/*
* -----来自百度百科对 realloc函数 的解释-----
* realloc原型是extern void *realloc(void *mem_address, unsigned int newsize);
* 先判断当前的指针指向的内存后面是否有足够的连续空间用于扩展,如果有,扩大mem_address指向的地址,
* 并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,
* 将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域
* (注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。
* 即重新分配存储器块的地址。
*/
assert(list);
list->data = temp;
// 别忘了更新 list 的大小
list->size += INCR_SIZE;
}
// 将index后面的元素往后移,为插入的元素腾出位置
int i;
for (i = list->count; i > index; i--) {
list->data[i] = list->data[i - 1];
}
list->data[index] = e;
list->count++;
// 返回 1 表示插入成功
return 1;
}
void list_add(struct list* list, int e) {
assert(list);
// 当在末尾添加元素时,数组满了的时候,调整内存大小,同 list_insert()
if (list->count >= list->size) {
int* temp = (int*) realloc(list->data, sizeof(int) * (list->size + INCR_SIZE));
list->data = temp;
list->size += INCR_SIZE;
}
list->data[list->count++] = e;
return;
}
int list_set(struct list* list, int index, int e) {
assert(list);
// 如果index无效,返回 0 表示设置失败
if (index < 0 || index > list->count) return 0;
list->data[index] = e;
return 1;
}
void list_remove(struct list* list, int index, int* e) {
assert(list);
// 如果index无效,返回 0 表示删除元素失败
if (index < 0 || index > list->count) return;
// 将删除后的元素值保存在 e 中
*e = list->data[index];
// 将index后面的元素往前移,填补删除后的空缺
int i;
for (i = index; i < list->count - 1; i++) {
list->data[i] = list->data[i + 1];
}
list->count--;
return;
}
int list_get(struct list* list, int index) {
assert(list);
assert(index >= 0 && index < list->count);
return list->data[index];
}
void list_print(struct list* list) {
assert(list);
int i;
for (i = 0; i < list->count - 1; i++) {
printf("%d, ", list->data[i]);
}
printf("%d\n", list->data[list->count - 1]);
return;
}