线性表
1. 顺序表
顺序表是指用一段连续的地址,依次存放数据元素的线性数据结构。此种存储方式使得顺序表的物理结构与逻辑结构都是连续的。 与数组的区别:函数中的数组被存放在栈段中,而栈段有系统限制的大小(可使用ulimit -s查看系统限制的大小,单位为KB),因此顺序表往往使用在堆段中操作管理动态数组的方式实现。
1.1 管理结点
在顺序表中,管理节点内部一般存放:数据域地址(*data)、**总容量(size)以及当前数据量(len)**等等。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Vector {
int *data;
int size;
int len;
} Vector;
Vector *initVector(int size){
Vector *v = (Vector *) malloc(sizeof(Vertor));
v->data = (int *) malloc(sizeof(int) * size);
v->len = 0;
v->size = size;
return v;
}
void freeVector(Vector *v){
if(!v) return;
free(v->data);
free(v);
}
int main(){
Vector *v = initVector(10)
return 0;
}
1.2 顺序表的插入
void insert(Vector *v, int idx, int val){
if(!v) return;
if(idx > v->len || idx < 0) return ;
if(v->len == v->size) return ;
for(int i = v->len; i > idx ;i++){
v->data[i] = v->data[i - 1];
}
v->data[i] = val;
v->len++;
}
1.3 顺序表的删除
void delete(Vector *v, int idx){
if(!v) return ;
if(idx >= v->len || idx < 0) return ;
for(int i = idx; i < v->len-1; i++){
v->data[i] = v->data[i + 1];
}
v->len--;
}
1.4 顺序表的扩容
扩容:新创建 原size * 2 的空间,然后将原数据从原空间迁移到新空间
int expand(Vector *v){
int exSize = v->size;
int *tmp;
while(exSize){
tmp = (int *) realloc(v->data, sizeof(int) * (v->size + exSize));
if(tmp) break;
exSize >>= 1;
}
if(!tmp) return 0;
v->data = tmp;
v->size += exSize;
return 1;
}
void insert(Vector *v, int idx, int val){
...
if(v->len == v->size) {
if(!expand(v)) return;
}
...
}
2. 链表
2.1 定义
链表是一种物理结构不连续,但可以依靠结点中的指针实现逻辑结构连续的线性数据结构。 构成链表的数据元素被称为“结点”,节点内部存放数据与指向下一个结点的指针。
typedef struct Node{
int val;
struct Node *next;
} Node;
Node *initNode(int val){
Node *node = (Node *) malloc(sizeof(Node));
node->val = val;
node->next = NULL;
return node;
}
void freeNode(Node *node){
if(!node) return ;
free(node);
}
只有单一结点指针的链表的全程是“单链表”,经常被简称为链表。 链表的管理结点一般仅需要存放头结点指针(*head)。 如果需要频繁获取链表长度,管理结点中可以额外存放链表长度(len)。
typedef struct List{
Node *head;
int len;
} List;
List *initList() {
List *list = (List *) malloc(sizeof(List));
list->head = NULL;
list->len = 0;
return list;
}
2.2 头部插入
头插法:将新插入的节点放在 head 后,时间复杂度O(1)
void insertToHead(List *list, int val){
if(!list) return ;
Node *node = initNode(val);
node->next = list->head;
list->head = node;
list->len++;
}
2.3 尾部插入
尾插法:找到最后一个结点,将新节点插入。如果没有设计尾指针,则时间复杂度为O(n)。
void insertToTail(List *list, int val){
if(!list) return ;
Node *node = initNode(val);
if(list->head == NULL){
list->head = node;
list->len++;
return ;
}
Node *p = list->head;
while(p->next != NUll){
p = p->next;
}
p->next = node;
list->len++;
}
int main(){
List *list1 = initList();
List *list2 = initList();
for(int i = 0;i <= 10;i += 2){
insertToHead(list1,i);
}
Node *p = list1->head;
while(p){
printf("%d -> ",p->val);
p = p->next;
}
printf("\n");
for(int i = 1;i <= 10;i += 2){
insertToTail(list2,i);
}
Node *q = list2->head;
while(q){
printf("%d -> ",q->val);
q = q->next;
}
printf("\n");
return 0;
}
输出结果:
2.4 任意位置插入
void insert(List *list, int idx, int val){
if(!list) return ;
if(idx > list->len || idx < 0) return ;
if(idx == 0){
insertToHead(list,val);
return;
}
Node *node = initNode(val);
Node *p = list->head;
for(int i = 1;i <= idx - 1;i++){
p = p->next;
}
node->next = p->next;
p->next = node;
list->len++;
}
2.5 任意位置删除
void remove(List *list, int idx){
if(!list) return ;
if(idx > list->len || idx < 0) return ;
Node *p = list->head;
if(idx == 0){
list->head = p->next;
list->len--;
freeNode(p);
return;
}
for(int i = 1;i <= idx - 1;i ++){
p = p->next;
}
Node *node = p->next;
p->next = p->next->next;
list->len--;
freeNode(node);
}
2.6 虚头结点
在需要特殊处理头结点的时候,可以实现一个带有虚头结点的链表。在链表初始化或在某些操作执行时,将一个额外的结点放在头指针执行的地方。虚头结点可以使得整个链表永远不空,永远有头。所以拥有虚头结点的链表在处理各类结点操作时会更加便捷。 任意位置插入:不需要特殊考虑插入位置是否在链表头部。 任意位置删除:不需要特殊考虑删除的结点是否是链表的第一个结点。
typedef struct Node{
int val;
struct Node *next;
} Node;
Node *initNode(int val){
Node *node = (Node *) malloc(sizeof(Node));
node->val = val;
node->next = NULL;
return node;
}
void freeNode(Node *node){
if(!node) return ;
free(node);
}
typedef struct List{
Node *head;
int len;
} List;
List *initList() {
List *list = (List *) malloc(sizeof(List));
list->head = initNode(-1);
list->len = 0;
return list;
}
void insertToHead(List *list, int val){
if(!list) return ;
Node *node = initNode(val);
node->next = list->head->next;
list->head->next = node;
list->len++;
}
void insertToTail(List *list, int val){
if(!list) return ;
Node *node = initNode(val);
Node *p = list->head;
while(p->next != NULL){
p = p->next;
}
p->next = node;
list->len++;
}
void insert(List *list, int idx, int val){
if(!list) return ;
if(idx > list->len || idx < 0) return ;
Node *node = initNode(val);
Node *p = list->head;
for(int i = 1;i <= idx;i++){
p = p->next;
}
node->next = p->next;
p->next = node;
list->len++;
}
void remove(List *list, int idx){
if(!list) return ;
if(idx > list->len || idx < 0) return ;
Node *p = list->head;
for(int i = 1;i <= idx;i ++){
p = p->next;
}
Node *node = p->next;
p->next = p->next->next;
list->len--;
freeNode(node);
}
|