当前位置: 首页 > news >正文

wordpress多站点多模板免费个人网站建设

wordpress多站点多模板,免费个人网站建设,博客网络营销方式,gogogo高清在线播放免费观看1线性表的定义与操作 1.1线性表的定义 线性表是一种基础的数据结构,其主要特点是:数据元素之间存在一种线性关系(一对一)。线性表的每一个数据元素都有一个唯一的前驱和后继,除了第一个元素没有前驱,最后…

1线性表的定义与操作

1.1线性表的定义

线性表是一种基础的数据结构,其主要特点是:数据元素之间存在一种线性关系(一对一)。线性表的每一个数据元素都有一个唯一的前驱和后继,除了第一个元素没有前驱,最后一个元素没有后继

  1. 有限性:线性表中数据元素的个数是有限的。
  2. 序列性:数据元素之间存在着先后顺序,即线性表中的数据元素是有顺序的。
  3. 相同数据类型:线性表中的数据元素具有相同的数据类型。

线性表(Linear List)是由n个数据元素组成的有限序列,可以用以下形式表示:

  • 顺序线性表:数据元素在内存中占据连续的存储空间。
  • 链式线性表:数据元素在内存中不必连续,但每个元素都包含指向下一个元素的地址(指针)

1.2线性表的基本操作

  1. 初始化操作

    • 功能:创建一个空的线性表,为后续的操作做好准备。
    • 实现:通常分配一定的存储空间,并将线性表的长度设置为 0。
  2. 插入操作

    • 功能:在指定位置插入一个新的数据元素。
    • 实现:首先要判断插入位置是否合法,如果合法,则从插入位置开始,将后面的元素依次向后移动一位,然后在指定位置插入新元素,并更新线性表的长度。
    • 例如,在线性表 [1, 2, 3] 中,在位置 2 插入元素 4,结果为 [1, 2, 4, 3]。
  3. 删除操作

    • 功能:删除指定位置的数据元素。
    • 实现:首先判断删除位置是否合法,如果合法,则将删除位置后面的元素依次向前移动一位,覆盖被删除的元素,并更新线性表的长度。
    • 例如,在线性表 [1, 2, 3, 4] 中,删除位置 2 的元素,结果为 [1, 2, 4]。
  4. 查找操作

    • 功能:在线性表中查找指定的数据元素,并返回其位置。
    • 实现:从线性表的第一个元素开始,依次比较每个元素与目标元素是否相等,直到找到目标元素或遍历完整个线性表。
    • 例如,在线性表 [1, 2, 3, 4] 中,查找元素 3,返回位置 2。
  5. 遍历操作

    • 功能:依次访问线性表中的每个数据元素。
    • 实现:使用循环从线性表的第一个元素开始,依次访问每个元素,直到遍历完整个线性表。
    • 例如,对于线性表 [1, 2, 3, 4],遍历输出为 “1 2 3 4”。
  6. 获取长度操作

    • 功能:返回线性表中数据元素的个数。
    • 实现:直接返回存储线性表长度的变量值。
  7. 判断空操作

    • 功能:判断线性表是否为空。
    • 实现:如果线性表的长度为 0,则为空,返回 true;否则返回 false。

2线性表的顺序存储

2.1顺序表的定义与初始化

一、顺序表的定义

顺序表,又称线性表的顺序存储结构,是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。在顺序表中,各个表项的逻辑顺序与其存放的物理顺序一致,即第i个表项存储于第i个物理位置(1≤i≤n)。

顺序表具有以下特点:

  • 随机访问:可通过首地址和元素序号在单位时间O(1)内找到指定的元素。
  • 存储密度高:因为每个结点存储空间只用来存储数据元素,没有额外的开销。
  • 物理位置相邻:逻辑上相邻的元素在物理位置上也相邻,因此插入和删除元素需要移动大量元素,比较耗时。

二、顺序表的初始化

  1. 确定顺序表的存储结构

    • 通常可以使用数组来实现顺序表。定义一个数组来存储数据元素,并记录顺序表的当前长度。
  2. 分配存储空间

    • 根据实际需求确定数组的大小,并在内存中分配相应的空间。可以在程序运行时动态分配空间,也可以使用固定大小的数组。
  3. 设置初始状态

    • 将顺序表的长度初始化为 0,表示当前没有数据元素。同时,可以将数组的所有元素初始化为特定的值,如 0 或 null。


以下是用 C 语言实现顺序表初始化的示例代码:

方式一:静态数组定义和初始化

这是最简单的定义和初始化方式,适用于固定大小的顺序表。

#include <stdio.h>#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型typedef struct {ElemType data[MAXSIZE];  // 存储数据的静态数组int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型// 初始化顺序表
void InitList(SqList *L) {L->length = 0;  // 初始化表长为0
}int main() {SqList L;  // 声明一个顺序表变量InitList(&L);  // 初始化顺序表// ... 其他操作return 0;
}

方式二:动态数组定义和初始化

这种方式允许在运行时动态分配内存,适用于不确定顺序表大小的情况。

#include <stdio.h>
#include <stdlib.h>typedef int ElemType;  // 定义顺序表中元素的类型typedef struct {ElemType *data;  // 存储数据的动态数组int length;  // 当前表长int maxSize;  // 数组的最大容量
} SqList;  // 定义顺序表的结构体类型// 初始化顺序表
void InitList(SqList *L, int maxSize) {L->data = (ElemType *)malloc(maxSize * sizeof(ElemType));  // 动态分配内存if (L->data == NULL) {printf("内存分配失败\n");exit(1);}L->length = 0;  // 初始化表长为0L->maxSize = maxSize;  // 设置最大容量
}// 释放顺序表的内存
void FreeList(SqList *L) {free(L->data);L->data = NULL;L->length = 0;L->maxSize = 0;
}int main() {SqList L;  // 声明一个顺序表变量InitList(&L, 100);  // 初始化顺序表,最大容量为100// ... 其他操作FreeList(&L);  // 释放顺序表的内存return 0;
}

方式三:内存分配与初始化函数结合

这种方式将内存分配和初始化结合在一起,适用于需要初始化默认值的情况。

#include <stdio.h>
#include <stdlib.h>typedef int ElemType;  // 定义顺序表中元素的类型typedef struct {ElemType *data;  // 存储数据的动态数组int length;  // 当前表长int maxSize;  // 数组的最大容量
} SqList;  // 定义顺序表的结构体类型// 初始化顺序表并分配内存
SqList* CreateList(int maxSize) {SqList *L = (SqList *)malloc(sizeof(SqList));  // 动态分配顺序表结构体内存if (L == NULL) {printf("内存分配失败\n");exit(1);}L->data = (ElemType *)malloc(maxSize * sizeof(ElemType));  // 动态分配数组内存if (L->data == NULL) {printf("内存分配失败\n");free(L);exit(1);}L->length = 0;  // 初始化表长为0L->maxSize = maxSize;  // 设置最大容量return L;
}// 释放顺序表的内存
void DestroyList(SqList *L) {free(L->data);free(L);
}int main() {SqList *L = CreateList(100);  // 创建顺序表,最大容量为100// ... 其他操作DestroyList(L);  // 释放顺序表的内存return 0;
}

2.2顺序表的基本操作

1插入运算

插入运算的步骤

  1. 检查位置合法性
    • 确保插入位置 pos 在有效范围内,即 1 ≤ pos ≤ length + 1,其中 length 是顺序表的当前长度。
    • 如果 pos 小于 1 或大于 length + 1,则插入位置非法,通常返回错误或抛出异常。
  2. 检查容量
    • 确保顺序表的容量(即数组的大小)足够容纳新元素。
    • 如果当前容量不足,需要进行扩容操作(通常是分配一个更大的数组,并将原数组的内容复制到新数组中)。
  3. 插入元素
    • 从插入位置 pos 开始,将顺序表中该位置及其后的所有元素向后移动一个位置,以腾出空间插入新元素。
    • 将新元素插入到 pos - 1 的位置(因为数组索引从 0 开始)。
  4. 更新长度
    • 将顺序表的长度 length 增加 1。

以下是一个简单的 c语言实现,展示了如何在顺序表中插入元素

#include <stdio.h>
#include <stdlib.h>#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型typedef struct {ElemType data[MAXSIZE];  // 存储数据的数组int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型// 初始化顺序表
void InitList(SqList *L) {L->length = 0;  // 初始化表长为0
}// 插入元素到顺序表的指定位置
int InsertList(SqList *L, int pos, ElemType e) {if (L->length >= MAXSIZE) {printf("顺序表已满,无法插入元素 %d\n", e);return 0;  // 插入失败}if (pos < 1 || pos > L->length + 1) {printf("插入位置 %d 不合法\n", pos);return 0;  // 插入失败}// 从后向前移动元素,为新元素腾出位置for (int i = L->length; i >= pos; i--) {L->data[i] = L->data[i - 1];}L->data[pos - 1] = e;  // 插入新元素L->length++;  // 更新当前表长return 1;  // 插入成功
}// 打印顺序表中的所有元素
void DisplayList(const SqList *L) {for (int i = 0; i < L->length; i++) {printf("%d ", L->data[i]);}printf("\n");
}int main() {SqList L;  // 声明一个顺序表变量InitList(&L);  // 初始化顺序表// 插入1到10的数字到顺序表中for (int i = 1; i <= 10; i++) {InsertList(&L, i, i);}// 打印顺序表中的所有元素printf("顺序表中的元素:");DisplayList(&L);// 在位置3插入一个新元素 99if (InsertList(&L, 3, 99)) {printf("插入元素后的顺序表:");DisplayList(&L);}return 0;
}
2删除运算

删除运算的步骤

  1. 检查位置合法性
    • 确保删除位置 pos 在有效范围内,即 1 ≤ pos ≤ length,其中 length 是顺序表的当前长度。
    • 如果 pos 小于 1 或大于 length,则删除位置非法,通常返回错误或抛出异常。
  2. 删除元素
    • 从删除位置 pos 开始,将顺序表中该位置之后的所有元素向前移动一个位置,以填补被删除元素留下的空位。
    • 注意,不需要为被删除元素分配新的存储空间或进行其他特殊处理,因为顺序表的内存是连续的,只需调整后续元素的位置即可。
  3. 更新长度
    • 将顺序表的长度 length 减少 1。
// 删除顺序表中指定位置的元素
int deleteElem(SeqList *L, int pos) {if (L->length == 0) {return 0; // 顺序表为空,删除失败}if (pos < 1 || pos > L->length) {return 0; // 位置不合法,删除失败}for (int i = pos; i < L->length; i++) {L->data[i - 1] = L->data[i];}L->length--;return 1; // 删除成功
}

以下是一个完整的 C 代码示例,包括顺序表的定义、初始化、删除运算以及显示顺序表内容的功能。

#include <stdio.h>
#include <stdlib.h>#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型typedef struct {ElemType data[MAXSIZE];  // 存储数据的数组int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型// 初始化顺序表
void InitList(SqList *L) {L->length = 0;  // 初始化表长为0
}// 插入元素到顺序表的指定位置
int InsertList(SqList *L, int pos, ElemType e) {if (L->length >= MAXSIZE) {printf("顺序表已满,无法插入元素 %d\n", e);return 0;  // 插入失败}if (pos < 1 || pos > L->length + 1) {printf("插入位置 %d 不合法\n", pos);return 0;  // 插入失败}// 从后向前移动元素,为新元素腾出位置for (int i = L->length; i >= pos; i--) {L->data[i] = L->data[i - 1];}L->data[pos - 1] = e;  // 插入新元素L->length++;  // 更新当前表长return 1;  // 插入成功
}// 删除顺序表中指定位置的元素
int DeleteList(SqList *L, int pos, ElemType *e) {if (L->length == 0) {printf("顺序表为空,无法删除元素\n");return 0;  // 删除失败}if (pos < 1 || pos > L->length) {printf("删除位置 %d 不合法\n", pos);return 0;  // 删除失败}*e = L->data[pos - 1];  // 保存被删除的元素// 从前向后移动元素,覆盖被删除的元素for (int i = pos - 1; i < L->length - 1; i++) {L->data[i] = L->data[i + 1];}L->length--;  // 更新当前表长return 1;  // 删除成功
}// 打印顺序表中的所有元素
void DisplayList(const SqList *L) {for (int i = 0; i < L->length; i++) {printf("%d ", L->data[i]);}printf("\n");
}int main() {SqList L;  // 声明一个顺序表变量InitList(&L);  // 初始化顺序表// 插入1到10的数字到顺序表中for (int i = 1; i <= 10; i++) {InsertList(&L, i, i);}// 打印顺序表中的所有元素printf("顺序表中的元素:");DisplayList(&L);// 从顺序表中删除位置3的元素ElemType deletedElem;if (DeleteList(&L, 3, &deletedElem)) {printf("删除了元素 %d\n", deletedElem);printf("删除元素后的顺序表:");DisplayList(&L);}return 0;
}
3按值查找

按值查找的步骤

  1. 遍历顺序表:从顺序表的第一个元素开始,逐个检查每个元素的值是否等于指定的值。

  2. 记录位置:如果找到了与指定值相等的元素,则记录该元素的位置(索引)。注意,根据具体实现,位置可能是从0开始还是从1开始的索引。

  3. 返回结果:如果遍历完整个顺序表后找到了元素,则返回记录的位置;如果未找到,则返回一个表示失败的值。

// 按值查找元素在顺序表中的位置
int searchElem(SeqList *L, int elem) {for (int i = 0; i < L->length; i++) {if (L->data[i] == elem) {return i + 1;}}return -1; // 未找到元素
}

 以下是一个简单的 c语言实现,展示了如何在顺序表中找到具体元素

#include <stdio.h>
#include <stdlib.h>#define MAXSIZE 100  // 定义顺序表的最大容量
typedef int ElemType;  // 定义顺序表中元素的类型typedef struct {ElemType data[MAXSIZE];  // 存储数据的数组int length;  // 当前表长
} SqList;  // 定义顺序表的结构体类型// 初始化顺序表
void InitList(SqList *L) {L->length = 0;  // 初始化表长为0
}// 插入元素到顺序表的指定位置
int InsertList(SqList *L, int pos, ElemType e) {if (L->length >= MAXSIZE) {printf("顺序表已满,无法插入元素 %d\n", e);return 0;  // 插入失败}if (pos < 1 || pos > L->length + 1) {printf("插入位置 %d 不合法\n", pos);return 0;  // 插入失败}// 从后向前移动元素,为新元素腾出位置for (int i = L->length; i >= pos; i--) {L->data[i] = L->data[i - 1];}L->data[pos - 1] = e;  // 插入新元素L->length++;  // 更新当前表长return 1;  // 插入成功
}// 按值查找顺序表中的元素
int LocateElem(const SqList *L, ElemType e) {for (int i = 0; i < L->length; i++) {if (L->data[i] == e) {return i + 1;  // 返回元素位置(从1开始)}}return 0;  // 未找到元素
}// 打印顺序表中的所有元素
void DisplayList(const SqList *L) {for (int i = 0; i < L->length; i++) {printf("%d ", L->data[i]);}printf("\n");
}int main() {SqList L;  // 声明一个顺序表变量InitList(&L);  // 初始化顺序表// 插入1到10的数字到顺序表中for (int i = 1; i <= 10; i++) {InsertList(&L, i, i);}// 打印顺序表中的所有元素printf("顺序表中的元素:");DisplayList(&L);// 按值查找元素 5int pos = LocateElem(&L, 5);if (pos) {printf("元素 5 的位置是 %d\n", pos);} else {printf("未找到元素 5\n");}// 按值查找元素 20pos = LocateElem(&L, 20);if (pos) {printf("元素 20 的位置是 %d\n", pos);} else {printf("未找到元素 20\n");}return 0;
}


3线性表的链式存储

优点

  1. 动态大小
    • 链表不需要在创建时指定大小,它可以根据需要动态地增长和缩小。
  2. 高效插入和删除
    • 在链表中,插入和删除操作(特别是在已知节点位置的情况下)通常可以在O(1)时间复杂度内完成,因为只需要调整相邻节点的指针。
  3. 无需连续内存
    • 链表节点可以分散在内存中的任何位置,这有助于减少内存碎片问题。
  4. 灵活性
    • 链表可以通过各种方式(如双向链表、循环链表等)进行扩展,以满足不同的需求。

缺点

  1. 访问速度慢
    • 链表不支持随机访问,因此查找特定位置的元素需要从头节点开始顺序遍历,时间复杂度为O(n)。
  2. 额外空间开销
    • 每个链表节点都需要额外的空间来存储指针,这增加了内存的使用量。
  3. 内存管理复杂
    • 链表节点的动态分配和释放需要仔细管理,以避免内存泄漏和碎片化。
  4. 缓存性能较差
    • 由于链表节点在内存中通常不是连续存储的,因此可能无法充分利用CPU缓存,导致访问速度下降。

应用场景

  • 链表适用于需要频繁进行插入和删除操作,但对随机访问要求不高的场景。
  • 双向链表和循环链表等扩展形式可以用于需要双向遍历或循环遍历的场景。
  • 在实现某些数据结构(如栈、队列、哈希表等)时,链表也可以作为底层存储结构。

3.1单向链表的结构

一、结构特点

  1. 节点组成
    • 单向链表由一系列节点(Node)组成,每个节点包含两个部分:数据域(Data)和指针域(Next)。
    • 数据域用于存储节点的数据,可以是任意类型,由编程者自行指定。
    • 指针域用于存储下一个节点的地址(或指针),指向链表中的下一个节点。
  2. 链接方向
    • 单向链表的链接方向是单向的,即只能从头节点开始,通过指针域依次访问后续节点。
  3. 头节点与头指针
    • 头节点是为了操作方便而设立的,通常放在第一个元素节点之前,其数据域一般无实际意义(但也可以用来存放链表长度等信息)。
    • 头指针是指向链表头节点的指针,无论链表是否为空,头指针均不为空,是链表的必要元素。
  4. 无头与有头链表
    • 无头链表:不设立头节点,头指针直接指向第一个元素节点(如果存在)。
    • 有头链表:设立头节点,头指针指向头节点,头节点的指针域指向第一个元素节点。

 二单链表的定义与初始化

 一、单链表的定义

typedef struct ListNode {int data;struct ListNode* next;
} ListNode;

 二、单链表的初始化

ListNode* initList() {// 创建一个空的头节点ListNode* head = (ListNode*)malloc(sizeof(ListNode));head->data = 0; // 头节点的数据域可以存储一些特殊信息,这里初始化为 0head->next = NULL;return head;
}

 以下是一个用C语言初始化单链表的示例(包含头节点):

#include <stdio.h>
#include <stdlib.h>// 定义链表节点
typedef int ElemType;  // 定义链表中元素的类型typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *LinkedList;// 初始化链表
LinkedList InitList() {LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);  // 内存分配失败,退出程序}L->next = NULL;  // 初始化头节点,指针域为空return L;  // 返回初始化后的链表
}// 测试初始化链表
int main() {LinkedList L = InitList();  // 初始化链表if (L->next == NULL) {printf("链表初始化成功,头节点指向 NULL\n");}// 这里可以继续对链表进行插入、删除等操作free(L);  // 释放头节点的内存return 0;
}

3.2单链表的基本操作

1插入结点建立单链表

在单链表中插入节点是一个常见的操作,它允许我们在链表的特定位置添加一个新的节点。

  1. 在链表头部插入节点:新节点成为链表的新头节点。
  2. 在链表尾部插入节点:新节点成为链表的最后一个节点。
  3. 在链表中间插入节点:在指定位置插入新节点,新节点位于指定位置的节点之前。

单链表的定义

#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;  // 定义链表中元素的类型typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *LinkedList;

初始化单链表

// 初始化链表
LinkedList InitList() {LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);  // 内存分配失败,退出程序}L->next = NULL;  // 初始化头节点,指针域为空return L;  // 返回初始化后的链表
}

在链表头部插入节点

// 在链表头部插入节点
void InsertHead(LinkedList L, ElemType e) {Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);  // 内存分配失败,退出程序}s->data = e;  // 新节点数据域赋值s->next = L->next;  // 新节点的指针域指向原头节点的下一个节点L->next = s;  // 头节点的指针域指向新节点
}

在链表尾部插入节点

// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {Node *p = L;// 找到链表的最后一个节点while (p->next) {p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);  // 内存分配失败,退出程序}s->data = e;  // 新节点数据域赋值s->next = NULL;  // 新节点的指针域指向NULLp->next = s;  // 原最后一个节点的指针域指向新节点
}

在指定位置插入节点

// 在链表的第pos个位置插入节点
int InsertAtPosition(LinkedList L, int pos, ElemType e) {Node *p = L;int i = 0;// 找到插入位置的前一个节点while (p && i < pos - 1) {p = p->next;i++;}if (!p || i > pos - 1) {printf("插入位置不合法\n");return 0;  // 插入失败}// 创建新节点Node *s = (Node *)malloc(sizeof(Node));if (!s) {printf("内存分配失败\n");exit(1);  // 内存分配失败,退出程序}s->data = e;  // 新节点数据域赋值s->next = p->next;  // 新节点的指针域指向插入位置的下一个节点p->next = s;  // 插入位置前一个节点的指针域指向新节点return 1;  // 插入成功
}

打印链表

// 打印链表中的所有元素
void DisplayList(LinkedList L) {Node *p = L->next;while (p) {printf("%d ", p->data);p = p->next;}printf("\n");
}

主函数

int main() {LinkedList L = InitList();  // 初始化链表// 在链表头部插入节点InsertHead(L, 1);InsertHead(L, 2);// 在链表尾部插入节点InsertTail(L, 3);InsertTail(L, 4);// 在指定位置插入节点InsertAtPosition(L, 3, 5);  // 在第3个位置插入元素5// 打印链表中的所有元素printf("链表中的元素:");DisplayList(L);return 0;
}
2有头节点的单链表

在有头节点的单链表中,链表包含一个额外的节点,称为头节点(或哑节点、哨兵节点)。头节点不存储实际的数据,而是作为链表的起点,其next指针指向链表的第一个实际数据节点。头节点的存在可以简化链表的操作,特别是在插入和删除节点时,因为它允许我们在链表头部进行这些操作而无需检查是否为空链表。

定义

在C语言中,有头节点的单链表可以通过定义一个结构体来表示链表节点,其中包含一个数据域和一个指向下一个节点的指针域。头节点也是这个结构体的一个实例,但其数据域通常不会被使用。

typedef struct Node {  int data; // 数据域,可以存储任意类型的数据,这里以int为例  struct Node* next; // 指针域,指向下一个节点  
} Node;

初始化

初始化有头节点的单链表时,需要为头节点分配内存,并将其next指针设置为NULL,表示链表当前为空。以下是一个初始化有头节点的单链表的函数示例:

Node* initLinkedList() {  Node* head = (Node*)malloc(sizeof(Node)); // 为头节点分配内存  if (head == NULL) {  // 内存分配失败,可以打印错误信息或进行其他错误处理  printf("内存分配失败\n");  return NULL; // 返回NULL表示初始化失败  }  head->next = NULL; // 初始化头节点的next指针为NULL,表示链表为空  return head; // 返回头节点的指针  
}
3求单链表的长度
#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *LinkedList;// 初始化有头节点的单链表
LinkedList InitList() {LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);}L->next = NULL;  // 初始化头节点的下一个节点为空return L;
}// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {Node *p = L;while (p->next) {p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);}s->data = e;      // 新节点数据域赋值s->next = NULL;   // 新节点的指针域指向NULLp->next = s;      // 原最后一个节点的指针域指向新节点
}// 求链表的长度
int GetLength(LinkedList L) {int length = 0;               // 初始化长度为0Node *p = L->next;           // 从头节点的下一个节点开始遍历while (p) {length++;                 // 计数p = p->next;             // 移动到下一个节点}return length;               // 返回链表的长度
}// 打印链表中的所有元素
void DisplayList(LinkedList L) {Node *p = L->next;while (p) {printf("%d ", p->data);p = p->next;}printf("\n");
}// 主函数
int main() {LinkedList L = InitList();  // 初始化链表// 插入一些元素到链表中InsertTail(L, 10);InsertTail(L, 20);InsertTail(L, 30);InsertTail(L, 40);// 打印链表printf("链表中的元素:");DisplayList(L);// 求链表的长度int length = GetLength(L);printf("链表的长度是:%d\n", length);return 0;
}
4输出表中所有元素
#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *LinkedList;// 初始化有头节点的单链表
LinkedList InitList() {LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);}L->next = NULL;  // 初始化头节点的下一个节点为空return L;
}// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {Node *p = L;while (p->next) {p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);}s->data = e;      // 新节点数据域赋值s->next = NULL;   // 新节点的指针域指向NULLp->next = s;      // 原最后一个节点的指针域指向新节点
}// 打印链表中的所有元素
void DisplayList(LinkedList L) {Node *p = L->next;  // 从头节点的下一个节点开始遍历while (p) {printf("%d ", p->data);  // 输出当前节点的数据域p = p->next;             // 移动到下一个节点}printf("\n");
}// 主函数
int main() {LinkedList L = InitList();  // 初始化链表// 插入一些元素到链表中InsertTail(L, 10);InsertTail(L, 20);InsertTail(L, 30);InsertTail(L, 40);// 打印链表中的所有元素printf("链表中的元素:");DisplayList(L);return 0;
}
5删除指定元素
#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *LinkedList;// 初始化有头节点的单链表
LinkedList InitList() {LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);}L->next = NULL;  // 初始化头节点的下一个节点为空return L;
}// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {Node *p = L;while (p->next) {p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);}s->data = e;      // 新节点数据域赋值s->next = NULL;   // 新节点的指针域指向NULLp->next = s;      // 原最后一个节点的指针域指向新节点
}// 删除链表中指定的元素
void DeleteElement(LinkedList L, ElemType e) {Node *p = L;  // p 指向头节点Node *q;      // q 用于指向要删除的节点// 遍历链表,找到目标元素的前一个节点while (p->next && p->next->data != e) {p = p->next;}// 如果找到了目标元素if (p->next) {q = p->next;           // q 指向要删除的节点p->next = q->next;     // 修改前一个节点的指针域,跳过目标节点free(q);               // 释放目标节点的内存空间printf("元素 %d 已删除\n", e);} else {printf("链表中不存在元素 %d\n", e);}
}// 打印链表中的所有元素
void DisplayList(LinkedList L) {Node *p = L->next;  // 从头节点的下一个节点开始遍历while (p) {printf("%d ", p->data);  // 输出当前节点的数据域p = p->next;             // 移动到下一个节点}printf("\n");
}// 主函数
int main() {LinkedList L = InitList();  // 初始化链表// 插入一些元素到链表中InsertTail(L, 10);InsertTail(L, 20);InsertTail(L, 30);InsertTail(L, 40);// 打印链表中的所有元素printf("删除前的链表:");DisplayList(L);// 删除指定元素DeleteElement(L, 30);// 打印删除后的链表printf("删除后的链表:");DisplayList(L);return 0;
}
6查找特定元素

1. 无头结点的单链表查找代码

struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 查找特定元素的函数
struct ListNode* search_element(struct ListNode* head, int target) {struct ListNode* current = head; // 从头节点开始查找while (current != NULL) { // 遍历链表直到结束if (current->value == target) { // 如果当前节点的值等于目标值return current; // 找到元素,返回当前节点}current = current->next; // 移动到下一个节点}return NULL; // 未找到元素,返回 NULL
}

 2有头结点的单链表查找代码

struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 查找特定元素的函数
struct ListNode* search_element(struct ListNode* head, int target) {struct ListNode* current = head->next; // 从头节点的下一个节点开始查找while (current != NULL) { // 遍历链表直到结束if (current->value == target) { // 如果当前节点的值等于目标值return current; // 找到元素,返回当前节点}current = current->next; // 移动到下一个节点}return NULL; // 未找到元素,返回 NULL
}

 3完整代码

#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *LinkedList;// 初始化有头节点的单链表
LinkedList InitList() {LinkedList L = (LinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);}L->next = NULL;  // 初始化头节点的下一个节点为空return L;
}// 在链表尾部插入节点
void InsertTail(LinkedList L, ElemType e) {Node *p = L;while (p->next) {p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);}s->data = e;      // 新节点数据域赋值s->next = NULL;   // 新节点的指针域指向NULLp->next = s;      // 原最后一个节点的指针域指向新节点
}// 查找链表中指定的元素
int FindElement(LinkedList L, ElemType e) {Node *p = L->next;  // 从头节点的下一个节点开始遍历int index = 1;      // 记录元素的索引位置// 遍历链表,查找目标元素while (p) {if (p->data == e) {return index;  // 返回元素的索引位置}p = p->next;       // 移动到下一个节点index++;           // 索引位置加1}return -1;  // 如果未找到,返回-1
}// 打印链表中的所有元素
void DisplayList(LinkedList L) {Node *p = L->next;  // 从头节点的下一个节点开始遍历while (p) {printf("%d ", p->data);  // 输出当前节点的数据域p = p->next;             // 移动到下一个节点}printf("\n");
}// 主函数
int main() {LinkedList L = InitList();  // 初始化链表// 插入一些元素到链表中InsertTail(L, 10);InsertTail(L, 20);InsertTail(L, 30);InsertTail(L, 40);// 打印链表中的所有元素printf("链表中的元素:");DisplayList(L);// 查找指定元素ElemType target = 30;int index = FindElement(L, target);if (index != -1) {printf("元素 %d 在链表中的位置是 %d\n", target, index);} else {printf("链表中不存在元素 %d\n", target);}return 0;
}
 7判断单链表是否为空

1. 无头结点的单链表判断是否为空

struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 判断无头结点单链表是否为空
int is_empty(struct ListNode* head) {return head == NULL; // 头节点为 NULL 则链表为空
}

2. 有头结点的单链表判断是否为空

struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 判断有头结点单链表是否为空
int is_empty(struct ListNode* head) {return head->next == NULL; // 如果头节点的下一个节点为 NULL,则链表为空
}
 8销毁单链表

1. 无头结点的单链表销毁

#include <stdlib.h>struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 销毁无头结点单链表的函数
void destroy_list(struct ListNode* head) {struct ListNode* current = head;struct ListNode* next_node;while (current != NULL) { // 遍历链表直到结束next_node = current->next; // 保存下一个节点free(current); // 释放当前节点current = next_node; // 移动到下一个节点}
}

2. 有头结点的单链表销毁

#include <stdlib.h>struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 销毁有头结点单链表的函数
void destroy_list(struct ListNode* head) {struct ListNode* current = head;struct ListNode* next_node;while (current != NULL) { // 遍历链表直到结束next_node = current->next; // 保存下一个节点free(current); // 释放当前节点current = next_node; // 移动到下一个节点}head = NULL; // 销毁后将头节点指针置为 NULL,避免野指针
}
9清空单链表

1. 无头结点的单链表清空

#include <stdlib.h>struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 清空无头结点单链表的函数
void clear_list(struct ListNode** head) {struct ListNode* current = *head;struct ListNode* next_node;while (current != NULL) { // 遍历链表直到结束next_node = current->next; // 保存下一个节点free(current); // 释放当前节点current = next_node; // 移动到下一个节点}*head = NULL; // 清空后将头节点指针置为 NULL,表示链表为空
}

2. 有头结点的单链表清空

#include <stdlib.h>struct ListNode {int value; // 节点的值struct ListNode* next; // 指向下一个节点的指针
};// 清空有头结点单链表的函数
void clear_list(struct ListNode* head) {struct ListNode* current = head->next;struct ListNode* next_node;while (current != NULL) { // 遍历链表直到结束next_node = current->next; // 保存下一个节点free(current); // 释放当前节点current = next_node; // 移动到下一个节点}head->next = NULL; // 清空后将头节点的 next 指针置为 NULL,表示链表为空
}

3.3循环链表

循环链表是一种特殊的链表结构,其特点是链表中的最后一个节点指向头节点,形成一个闭环。这种结构使得链表的遍历可以从任何节点开始,并且在遍历到链表末尾时可以无缝地回到链表的开头。循环链表可以是单向的(每个节点只有一个指针指向下一个节点)或双向的(每个节点有两个指针,分别指向前一个节点和后一个节点)。

循环链表的特点

  1. 闭环结构:链表的最后一个节点指向头节点,形成一个闭环。
  2. 遍历灵活:可以从任何节点开始遍历,并且在遍历到链表末尾时可以回到链表的开头。
  3. 节省空间:相比于双向链表,单向循环链表只需要一个指针域,节省了空间。
  4. 应用场景:常用于需要循环操作的场景,如循环队列、约瑟夫环问题等。

循环链表的基本操作

1. 初始化循环链表

初始化一个空的循环链表,创建一个头节点,并使其指向自身。

#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;typedef struct Node {ElemType data;      // 数据域struct Node *next;  // 指针域,指向下一个节点
} Node, *CircularLinkedList;// 初始化循环链表
CircularLinkedList InitCircularList() {CircularLinkedList L = (CircularLinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);}L->next = L;  // 头节点的下一个节点指向自身,形成闭环return L;
}

2. 插入节点

在循环链表中插入节点,可以选择在链表的头部或尾部插入。

// 在链表尾部插入节点
void InsertTail(CircularLinkedList L, ElemType e) {Node *p = L;while (p->next != L) {  // 找到链表的最后一个节点p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);}s->data = e;      // 新节点数据域赋值s->next = L;      // 新节点的指针域指向头节点p->next = s;      // 原最后一个节点的指针域指向新节点
}

3. 删除节点

删除循环链表中的指定节点。

// 删除链表中指定的元素
void DeleteElement(CircularLinkedList L, ElemType e) {Node *p = L;  // p 指向头节点Node *q;      // q 用于指向要删除的节点// 遍历链表,找到目标元素的前一个节点while (p->next != L && p->next->data != e) {p = p->next;}// 如果找到了目标元素if (p->next != L) {q = p->next;           // q 指向要删除的节点p->next = q->next;     // 修改前一个节点的指针域,跳过目标节点free(q);               // 释放目标节点的内存空间printf("元素 %d 已删除\n", e);} else {printf("链表中不存在元素 %d\n", e);}
}

4. 查找节点

查找循环链表中的指定元素。

// 查找链表中指定的元素
int FindElement(CircularLinkedList L, ElemType e) {Node *p = L->next;  // 从头节点的下一个节点开始遍历int index = 1;      // 记录元素的索引位置// 遍历链表,查找目标元素while (p != L) {if (p->data == e) {return index;  // 返回元素的索引位置}p = p->next;       // 移动到下一个节点index++;           // 索引位置加1}return -1;  // 如果未找到,返回-1
}

5. 打印链表

遍历并打印循环链表中的所有元素。

// 打印链表中的所有元素
void DisplayList(CircularLinkedList L) {Node *p = L->next;  // 从头节点的下一个节点开始遍历while (p != L) {printf("%d ", p->data);  // 输出当前节点的数据域p = p->next;             // 移动到下一个节点}printf("\n");
}

主函数示例

// 主函数
int main() {CircularLinkedList L = InitCircularList();  // 初始化循环链表// 插入一些元素到链表中InsertTail(L, 10);InsertTail(L, 20);InsertTail(L, 30);InsertTail(L, 40);// 打印链表中的所有元素printf("链表中的元素:");DisplayList(L);// 查找指定元素ElemType target = 30;int index = FindElement(L, target);if (index != -1) {printf("元素 %d 在链表中的位置是 %d\n", target, index);} else {printf("链表中不存在元素 %d\n", target);}// 删除指定元素DeleteElement(L, 30);// 打印删除后的链表printf("删除后的链表:");DisplayList(L);return 0;
}

3.4双向链表

双向链表是一种链表结构,与单向链表不同,双向链表中的每个节点除了包含数据域外,还包含两个指针,分别指向前一个节点和后一个节点。这种结构使得链表的操作更加灵活,但也增加了空间的开销。

双向链表的特点

  1. 双向遍历:可以从任何一个节点开始向前或向后遍历链表。
  2. 灵活性高:插入和删除操作更加方便,因为可以直接访问前一个节点和后一个节点。
  3. 空间开销大:每个节点包含两个指针,相比于单向链表,空间开销更大。
  4. 应用场景:适用于需要频繁插入和删除操作的场景,如文本编辑器中的撤销/重做功能。

双向链表的基本操作

1. 初始化双向链表

初始化一个空的双向链表,创建一个头节点,并使其前后指针都指向自身(如果链表为空)。

#include <stdio.h>
#include <stdlib.h>// 定义链表节点的类型
typedef int ElemType;typedef struct Node {ElemType data;      // 数据域struct Node *prev;  // 指针域,指向前一个节点struct Node *next;  // 指针域,指向下一个节点
} Node, *DoubleLinkedList;// 初始化双向链表
DoubleLinkedList InitDoubleList() {DoubleLinkedList L = (DoubleLinkedList)malloc(sizeof(Node));  // 申请头节点空间if (!L) {printf("内存分配失败\n");exit(1);}L->prev = L;  // 头节点的前指针指向自身L->next = L;  // 头节点的后指针指向自身return L;
}

2. 插入节点

在双向链表中插入节点,可以选择在链表的头部或尾部插入。

// 在链表尾部插入节点
void InsertTail(DoubleLinkedList L, ElemType e) {Node *p = L;while (p->next != L) {  // 找到链表的最后一个节点p = p->next;}Node *s = (Node *)malloc(sizeof(Node));  // 申请新节点空间if (!s) {printf("内存分配失败\n");exit(1);}s->data = e;      // 新节点数据域赋值s->next = L;      // 新节点的后指针指向头节点s->prev = p;      // 新节点的前指针指向原最后一个节点p->next = s;      // 原最后一个节点的后指针指向新节点L->prev = s;      // 头节点的前指针指向新节点
}

3. 删除节点

删除双向链表中的指定节点。

// 删除链表中指定的元素
void DeleteElement(DoubleLinkedList L, ElemType e) {Node *p = L->next;  // p 指向第一个节点// 遍历链表,找到目标元素while (p != L && p->data != e) {p = p->next;}// 如果找到了目标元素if (p != L) {p->prev->next = p->next;  // 修改前一个节点的后指针p->next->prev = p->prev;  // 修改后一个节点的前指针free(p);                  // 释放目标节点的内存空间printf("元素 %d 已删除\n", e);} else {printf("链表中不存在元素 %d\n", e);}
}

4. 查找节点

查找双向链表中的指定元素。

// 查找链表中指定的元素
int FindElement(DoubleLinkedList L, ElemType e) {Node *p = L->next;  // 从第一个节点开始遍历int index = 1;      // 记录元素的索引位置// 遍历链表,查找目标元素while (p != L) {if (p->data == e) {return index;  // 返回元素的索引位置}p = p->next;       // 移动到下一个节点index++;           // 索引位置加1}return -1;  // 如果未找到,返回-1
}

5. 打印链表

遍历并打印双向链表中的所有元素。

// 打印链表中的所有元素
void DisplayList(DoubleLinkedList L) {Node *p = L->next;  // 从第一个节点开始遍历while (p != L) {printf("%d ", p->data);  // 输出当前节点的数据域p = p->next;             // 移动到下一个节点}printf("\n");
}

主函数示例

// 主函数
int main() {DoubleLinkedList L = InitDoubleList();  // 初始化双向链表// 插入一些元素到链表中InsertTail(L, 10);InsertTail(L, 20);InsertTail(L, 30);InsertTail(L, 40);// 打印链表中的所有元素printf("链表中的元素:");DisplayList(L);// 查找指定元素ElemType target = 30;int index = FindElement(L, target);if (index != -1) {printf("元素 %d 在链表中的位置是 %d\n", target, index);} else {printf("链表中不存在元素 %d\n", target);}// 删除指定元素DeleteElement(L, 30);// 打印删除后的链表printf("删除后的链表:");DisplayList(L);return 0;
}

 总结:

比较项目顺序表链表
存储方式顺序存储,用一段连续的内存空间存储数据元素。链式存储,通过指针将各个数据元素链接起来。
随机访问支持随机访问,可以在 O (1) 时间内访问任意位置的元素。不支持随机访问,访问特定位置元素需要从链表头开始遍历,时间复杂度为 O (n)。
插入和删除操作在中间位置插入和删除元素时,需要移动大量元素,时间复杂度为 O (n)。在中间位置插入和删除元素只需修改指针,时间复杂度为 O (1)。
内存占用需要预先分配连续的内存空间,如果空间不足需要进行扩容,可能导致大量数据的移动;空间较为紧凑,没有额外指针占用。按需分配内存,不会出现内存浪费,但每个节点都需要额外的指针空间,相对来说空间占用可能稍大。
空间复杂度如果预先分配的空间过大,可能会造成空间浪费;如果空间不足进行扩容,也会有一定开销。一般为 O (n)。每个节点都需要额外指针,空间开销相对稳定。一般也为 O (n)。
适用场景适合频繁访问元素,而插入和删除操作较少的情况。适合频繁进行插入和删除操作的情况。

 

比较项目单链表循环链表单向循环链表双向循环链表
存储方式通过指针将各个数据元素链接起来,尾节点指向 NULL。首尾相接形成环状结构。与循环链表类似,只是单向循环链表更强调其单向性。通过两个指针分别指向前驱和后继,首尾相接形成环状结构。
遍历方式从链表头开始,顺着指针依次访问每个节点,直到尾节点(指向 NULL)。从链表中任意一个节点开始,顺着指针可以循环访问所有节点。与循环链表类似,单向遍历。从链表中任意一个节点开始,可以顺着指针向前或向后循环访问所有节点。
随机访问不支持随机访问,访问特定位置元素需要从链表头开始遍历,时间复杂度为 O (n)。不支持随机访问,时间复杂度为 O (n)。不支持随机访问,时间复杂度为 O (n)。不支持随机访问,时间复杂度为 O (n)。
插入操作在中间位置插入元素需修改前后节点的指针,时间复杂度为 O (1)(已知插入位置的情况下)。在中间位置插入元素需修改前后节点的指针,时间复杂度为 O (1)(已知插入位置的情况下)。与循环链表类似,时间复杂度为 O (1)。在中间位置插入元素需修改前后节点的指针,时间复杂度为 O (1)(已知插入位置的情况下)。
删除操作在中间位置删除元素需修改前后节点的指针,时间复杂度为 O (1)(已知删除位置的情况下)。在中间位置删除元素需修改前后节点的指针,时间复杂度为 O (1)(已知删除位置的情况下)。与循环链表类似,时间复杂度为 O (1)。在中间位置删除元素需修改前后节点的指针,时间复杂度为 O (1)(已知删除位置的情况下)。
适用场景适用于插入和删除操作相对较少,对遍历顺序没有特殊要求的情况。适用于需要循环遍历的场景,如某些算法中需要反复遍历链表。与循环链表类似,在需要单向循环遍历的场景中适用。适用于需要双向遍历以及频繁在链表中进行插入和删除操

文章转载自:
http://moorwort.kryr.cn
http://slantendicular.kryr.cn
http://intimidatory.kryr.cn
http://incuriosity.kryr.cn
http://nye.kryr.cn
http://postmaster.kryr.cn
http://chatoyancy.kryr.cn
http://domineering.kryr.cn
http://spectacularity.kryr.cn
http://microfiche.kryr.cn
http://montonero.kryr.cn
http://solatia.kryr.cn
http://peat.kryr.cn
http://lampyrid.kryr.cn
http://euphemistic.kryr.cn
http://intermittent.kryr.cn
http://fluoropolymer.kryr.cn
http://thermometry.kryr.cn
http://sealery.kryr.cn
http://predynastic.kryr.cn
http://analgesia.kryr.cn
http://annie.kryr.cn
http://ideational.kryr.cn
http://coproduct.kryr.cn
http://wraaf.kryr.cn
http://brougham.kryr.cn
http://sightsinging.kryr.cn
http://subemployment.kryr.cn
http://calceiform.kryr.cn
http://inboard.kryr.cn
http://gagwriter.kryr.cn
http://fosterage.kryr.cn
http://lear.kryr.cn
http://undiscipline.kryr.cn
http://sacciform.kryr.cn
http://reachless.kryr.cn
http://deplorably.kryr.cn
http://mourning.kryr.cn
http://tervalent.kryr.cn
http://tailsitter.kryr.cn
http://capitalistic.kryr.cn
http://sheerly.kryr.cn
http://incaution.kryr.cn
http://heidi.kryr.cn
http://allay.kryr.cn
http://differ.kryr.cn
http://hedonism.kryr.cn
http://crosspatch.kryr.cn
http://injector.kryr.cn
http://cumulocirrus.kryr.cn
http://yeld.kryr.cn
http://tiewig.kryr.cn
http://radiosodium.kryr.cn
http://stalemate.kryr.cn
http://discipular.kryr.cn
http://roussillon.kryr.cn
http://paperbark.kryr.cn
http://acetazolamide.kryr.cn
http://subscribe.kryr.cn
http://oddment.kryr.cn
http://beatle.kryr.cn
http://dipleurogenesis.kryr.cn
http://pedochemical.kryr.cn
http://nibs.kryr.cn
http://selenodesy.kryr.cn
http://fidelity.kryr.cn
http://tollman.kryr.cn
http://rebuttable.kryr.cn
http://universalizable.kryr.cn
http://canonical.kryr.cn
http://medullin.kryr.cn
http://dressy.kryr.cn
http://justiceship.kryr.cn
http://pahlavi.kryr.cn
http://meninx.kryr.cn
http://lyddite.kryr.cn
http://goddaughter.kryr.cn
http://swampland.kryr.cn
http://plantsman.kryr.cn
http://indraft.kryr.cn
http://lxx.kryr.cn
http://airship.kryr.cn
http://retinospora.kryr.cn
http://buddhism.kryr.cn
http://wrap.kryr.cn
http://musically.kryr.cn
http://genie.kryr.cn
http://morra.kryr.cn
http://choora.kryr.cn
http://appd.kryr.cn
http://smiling.kryr.cn
http://canonicals.kryr.cn
http://pigtailed.kryr.cn
http://lymphotoxin.kryr.cn
http://asphaltite.kryr.cn
http://marginalia.kryr.cn
http://doozy.kryr.cn
http://seminoma.kryr.cn
http://mitosis.kryr.cn
http://creatinuria.kryr.cn
http://www.15wanjia.com/news/67036.html

相关文章:

  • php第一季网站开发实例教程sem推广竞价托管
  • 深圳建设局招标网站chrome 谷歌浏览器
  • 成都网站建设waibaoweb四川企业seo
  • 杭州网站推广方式什么搜索引擎搜索最全
  • wordpress 字体 服务器seo模拟点击有用吗
  • 深圳做网站的网怎么做百度推广
  • 电子商务网站建设考题广州seo网络推广员
  • 网站开发常用模板天津百度网站快速优化
  • 天津网页模板建站福州百度开户多少钱
  • 南昌做网站开发的公司哪家好手机网页制作
  • 政府网站官网360推广联盟
  • 做网站都需要什么贴吧今日广东头条新闻
  • 做宣传册参考的网站武汉最新今天的消息
  • wordpress 调用数据库推广优化师
  • 关于网站开发的期刊推广发帖网站
  • 做化妆品网站怎样百度邮箱登录入口
  • 淮阴区建设局网站东莞seo技术
  • 网站收录少了注册网站流程和费用
  • 网站建设费用扬中网站制作
  • 江阴青阳道路建设网站今天热搜榜前十名
  • 南京市建设工程网站上海网络营销推广外包
  • 上海建设工程咨询网证书查询seo推广公司哪家好
  • 深圳css3网站开发多少钱百度广告投放技巧
  • 网站备案问题制作一个网页的步骤
  • 黄山旅游攻略 知乎资源优化排名网站
  • 网站的建设与管理暂行办法杭州seo网站推广
  • 关键词推广优化厦门关键词优化平台
  • wordpress能否做网站品牌宣传策略
  • 免费网站奖励自己游戏淘宝关键词搜索量排名
  • html网页设计结课作业北京搜索引擎优化