Key 1:线性表是指各数据元素间保持“1对1”关系的数据结构
Key 2:线性表分为顺序表(数组)和链表,分别通过元素相邻和保存指针域的方式来实现“1对1”
查找(定位)元素:按值查找
顺序表新增 / 删除元素
给定长度为 n 的顺序表,在指定位置 i 插入一个新元素
需要将位置 i 到位置 n - 1的所有元素都向后或向前挪一格
在最坏情况(i = 0)下,需要挪动全部的 n 个元素 => 时间复杂度为O(n)
无需利用额外空间 => 空间复杂度为O(1)
(单)链表新增元素
首先需要从头结点开始逐个向后找 i - 1次 => 时间复杂度为O(n)
找到后插入只需要修改第 i - 1个结点和待插入节点的 [后继结点地址] 即可 => O(1)
无需利用额外空间 => 空间复杂度为O(1)
(单)链表删除元素
需要移动到第 i 个结点的前驱结点,最坏情况下移动 n - 1次 => 时间复杂度为O(n)
修改前驱结点的后继指针 => O(1)
无需利用额外空间 => 空间复杂度为O(1)
顺序表更新元素
(单)链表更新元素
Key 1:顺序表中增加和删除一个元素将导致该位置后的元素前移或后移,复杂度为O(n)
Key 2:单链表增加和删除元素虽然不用移动元素,但需先找到其前驱结点,复杂度为O(n)
Key 3:若线性表需要频繁更新元素 -> 选择用顺序表实现(数组)
Key 4:若线性表需要频繁插入删除元素 -> 选择用链式表实现
合并两个有序表:本来分别有序,合并结果仍然有序
合并两个有序顺序表
合并两个有序单链表
Key 1:合并两个有序表:逐一比较两表当前元素,将正确的元素添加进结果表并移动游标
main.c
#include
#include
#include "sqlist.c"
int main()
{
sqlist list;
sqlist_init(&list, 100);
int i;
//测试插入
for (i = 1; i < 10; i++)
{
sqlist_insert(&list, 0, i);
sqlist_print(&list);
}
sqlist_insert(&list, 9, 33);
sqlist_print(&list);
int r;
r = sqlist_insert(&list, 11, 44);
printf("invaild insert ret = %d\n", r);
sqlist_print(&list);
//size函数测试
i = sqlist_size(&list);
printf("list size = %d \n", i);
//测试查找
i = sqlist_search(&list, 5);
printf("pos of 5 is %d \n", i);
int v;
//测试删除
for (i = 0; i < 10; i++)
{
sqlist_delete(&list, 1, &v);
printf("delete ret is %d \n", v);
sqlist_print(&list);
}
//测试empty函数
i = sqlist_empty(&list);
printf("is empty? %d\n", i);
//测试full函数
i = sqlist_full(&list);
printf("is full? %d\n", i);
sqlist_destory(&list);
return 0;
}
sqlist.h
#ifndef _SQLIST_H_
#define _SQLIST_H_
typedef struct sqlist
{
int size; //空间大小
int n; //当前数组中元素个数
int* data; //数组 int data[100]
} sqlist;
int sqlist_init(sqlist* list, int size);
int sqlist_destory(sqlist* list);
//pos = 0插入到最前面 pos = 1插入到第一个元素后面
int sqlist_insert(sqlist* list, int pos, int value);
int sqlist_delete(sqlist* list, int pos, int* outv);
void sqlist_print(sqlist* list);
int sqlist_size(sqlist* list);
int sqlist_search(sqlist* list, int v);
int sqlist_empty(sqlist* list);
int sqlist_full(sqlist* list);
#endif
sqlist.c
#include "sqlist.h"
#include
int sqlist_init(sqlist* list, int size)
{
list->size = size;
list->n = 0;
list->data = (int*)malloc(size * sizeof(int));
return list->data != NULL; //如果分配内存成功,返回1,否则返回0
}
int sqlist_destory(sqlist* list)
{
//安全隐患,在堆溢出中有个很著名的溢出方法叫做double free
if (list->data == NULL)
{
return 0;
}
free(list->data);
list->data = NULL;
return 1;
}
int sqlist_insert(sqlist* list, int pos, int value)
{
int i;
if (pos < 0 || pos > list->n)
{
return 0; //位置不合法
}
if (list->n == list->size)
{
return -1; //内存不够了。
}
//通过个案分析的方法确定循环的起始和结束!!!!!
for (i = list->n; i > pos; i--)
{
list->data[i] = list->data[i - 1];
}
list->data[pos] = value; //一定要测试一下!!!!!
list->n++;
return 1; //插入成功
}
void sqlist_print(sqlist* list)
{
int i;
printf("sqlist: size = %d, n = %d [ ", list->size, list->n);
for (i = 0; i < list->n; i++)
{
printf("%d ", list->data[i]);
}
printf("]\n");
}
//pos >= 1 <= n
int sqlist_delete(sqlist* list, int pos, int* outv)
{
if (pos < 1 || pos > list->n)
{
return 0; //参数非法!!
}
if (list->n == 0)
{
return -1; //内容不够了
}
*outv = list->data[pos - 1];
int i;
//个案分析,着重分析循环的开始和结束
//具体做法就是代入法。n = 3 pos = 1, date 0 = datee 1, date 1 = date 2
for (i = pos; i < list->n; i++)
{
list->data[i - 1] = list->data[i];
}
list->n--;
return 1; //删除成功
}
int sqlist_size(sqlist* list)
{
return list->n;
}
//返回数组的下标还是逻辑下标? 返回逻辑下标
int sqlist_search(sqlist* list, int v)
{
int i;
for (i = 0; i < list->n; i++)
{
if (list->data[i] == v)
{
return i + 1;
}
}
return 0; //找不到
}
int sqlist_empty(sqlist* list)
{
return list->n == 0;
}
int sqlist_full(sqlist* list)
{
return list->n == list->size;
}
main.c
#include
#include
#include "linkedlist.c"
int main()
{
int i, x;
node* head = linkedlist_init();
//判空
x = linkedlist_empty(head);
printf("empty return %d\n", x);
//测试插入
for (i = 1; i < 9; i++)
{
linkedlist_insert(head, 0, i);
}
linkedlist_print(head);
linkedlist_insert(head, 8, 99);
linkedlist_print(head);
linkedlist_insert(head, 18, 99);
linkedlist_print(head);
//测试删除
printf("delete:");
for (i = 0; i < 5; i++)
{
linkedlist_delete(head, 3, &x);
printf("%d ", x);
}
printf("\n");
linkedlist_print(head);
//测试size
printf("size = %d\n", linkedlist_size(head));
//判空
x = linkedlist_empty(head);
printf("empty return %d\n", x);
//测试搜索
printf("pos of 99 is %d\n", linkedlist_search(head, 99));
linkedlist_destory(head);
return 0;
}
linkedlist.h
#ifndef _LINKED_LIST_H
#define _LINKED_LIST_H
typedef struct node
{
int data; //数据
struct node* next; //指向下一个节点的指针
} node;
node* linkedlist_init();
void linkedlist_destory(node* head);
int linkedlist_insert(node* head, int pos, int x);
//返回值为是否成功运行,1成功,0失败,删除的节点放在x中。
int linkedlist_delete(node* head, int pos, int* x);
void linkedlist_print(node* head);
int linkedlist_size(node* head);
int linkedlist_search(node* head, int x);
int linkedlist_empty(node* head);
#endif
linkedlist.c
#include "linkedlist.h"
#include
//初始化 带有头结点的单链表
// h -> [ head ] -> [ first ] -> [ second ] -> [...] -> [ ]
node* linkedlist_init()
{
node* p = (node*)malloc(sizeof(node));
p->data = 0;
p->next = NULL;
return p; //这就是头结点,返回他
}
void linkedlist_destory(node* head)
{
node* p;
while (head != NULL)
{
p = head->next; //找到下一个节点
free(head);
head = p; // head指向原本的下一个节点
}
}
// h -> [ head ] ->[1]->[2]->[3]->[4]
// 插入的位置,给0插入到1之前,给1插入到1之后
int linkedlist_insert(node* head, int pos, int x)
{
int i;
if (pos < 0)
return 0;// 失败
node* p = head;// p应该指向插入位置前一个
for (i = 0; i < pos; i++)
{
if (p == NULL)
{
return 0;// 失败
}
p = p->next; // 向后移动一格
}
node* nn = (node*)malloc(sizeof(node));
nn->next = p->next;
nn->data = x;
p->next = nn;
return 1;// 成功
}
// h -> [ head ] ->[1]->[2]->[3]->[4]
// 假设pos从1开始编号
int linkedlist_delete(node* head, int pos, int* x)
{
int i;
node* p = head;
// 验证循环次数对不对,就是通过代数的方法
// 假设pos = 1,看他执行多少次,p最终指向了哪一个元素。
for (i = 0; i < pos - 1; i++)
{
if (p == NULL)
{
return 0;// pos不合法,删除失败
}
p = p->next;// 向后一格
}
node* q = p->next;// 这就是要删除的节点
*x = q->data;// 取出数据放入传出参数
p->next = q->next;
free(q);
return 1;// 成功
}
void linkedlist_print(node* head)
{
node* p = head->next;// 指向第一个有数据的节点
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
// h -> [ head ] ->[1]->[2]->[3]->[4]
int linkedlist_size(node* head)
{
node* p = head->next;
int c = 0;
while (p != NULL)
{
c++;
p = p->next;
}
return c;
}
//根据值x获取位置,是一个从1开始编号的下标
int linkedlist_search(node* head, int x)
{
node* p = head->next;
int pos = 1;
while (p != NULL)
{
if (p->data == x)
{
return pos;
}
p = p->next;
pos++;
}
return 0; // 找不到返回0
}
int linkedlist_empty(node* head)
{
return head->next == NULL;
}