数据结构与算法之美学习笔记(二):链表
链表
链表与数组区别:
从底层的存储结构上来看,数组需要一块连续的内存空间来存储,对内存的要求比较高。如果我们申请一个100MB大小的数组,当内存中没有连续的、足够大的存储空间时,即便内存的剩余总可用空间大于100MB,仍然会申请失败。 而链表恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用,所以如果我们申请的是100MB大小的链表,根本不会有问题。
链表结构五花八门,三种最常见的链表结构:单链表、双向链表和循环链表。
链表插入和删除不需要搬运数据,只要修改指针,所以时间复杂度为O(1)。但是链表要想随机访问第k个元素,没有数组那么高效了。因为链表中的数据并非连续存储的,所以无法像数组那样,根据首地址和下标,通过寻址公式就能直接计算出对应的内存地址,而是需要根据指针一个结点一个结点地依次遍历,直到找到相应的结点。
单链表的基本操作(头插法/尾插法创建单链表、插入、删除),删除链表倒数第k个结点以及合并两个有序链表:
#include <iostream> #include <stdio.h> #include <stdlib.h> /* * 单链表结构体定义; * 头插法/尾插法; * 插入、删除;
* 删除倒数第k个结点
* 归并两个有序链表 */ //单链表结构体 typedef struct LinkList { int data; LinkList* next; }Node; Node* init_createa(int n) { Node *head, *p, *q; head = (Node *)malloc(sizeof(Node)); head->data = NULL; head->next = NULL; q = head; for (int i = 0; i < n; i++) { p = (Node *)malloc(sizeof(Node)); p->data = 2 * i + 1; p->next = NULL; q->next = p; q = p; } return head; } Node* init_createb(int n) { Node *head, *p, *q; head = (Node *)malloc(sizeof(Node)); head->data = NULL; head->next = NULL; q = head; for (int i = 0; i < n; i++) { p = (Node *)malloc(sizeof(Node)); p->data = 2 * i; p->next = NULL; q->next = p; q = p; } return head; } //尾插法初始单链表 Node* init_tailcreate(int n) { Node *head, *p, *q; head = (Node *)malloc(sizeof(Node)); head->data = NULL; head->next = NULL; q = head; for (int i = 0; i < n; i++) { p = (Node *)malloc(sizeof(Node)); p->data = 2 * i + 1; p->next = NULL; q->next = p; q = p; } return head; } //头插法初始单链表 Node* init_headcreate(int n) { Node *head, *p, *q; head = (Node *)malloc(sizeof(Node)); head->data = NULL; head->next = NULL; q = head; while(n) { p = (Node *)malloc(sizeof(Node)); p->data = n; p->next = NULL; p->next = q->next; q->next = p; n--; } return head; } //在链表位置pos后插入数据 Node* insert_data(Node *head_node, int pos, int data) { Node *p, *q; p = head_node; q = (Node *)malloc(sizeof(Node)); q->data = data; q->next = NULL; while (pos--) { p = p->next; } q->next = p->next; p->next = q; return head_node; } //删除pos后的数据 Node* delete_data(Node* head_node, int pos) { Node *p, *q; p = head_node; while (pos--) { p = p->next; } q = p->next; p->next = q->next; q->next = NULL; free(q); return head_node; } //删除倒数第pos个的数据 Node* delete_data_intail(Node* head_node, int pos) { Node *p, *q; p = head_node; q = head_node; while (pos--) { p = p->next; } while (p->next != NULL) { q = q->next; p = p->next; } Node *tem = q->next; q->next = q->next->next; tem->next = NULL; free(tem); return head_node; } //合并两个有序链表 Node* merge_sort_list(Node* head_a, Node* head_b) { Node *a, *b, *head, *p; a = head_a->next; b = head_b->next; head = (Node *)malloc(sizeof(Node)); head->data = NULL; head->next = NULL; p = head; while(a != NULL && b != NULL) { if ( a->data < b->data) { p->next = a; a = a->next; } else if ( b->data < a->data) { p->next = b; b = b->next; } p = p->next; } if (a != NULL) p->next = a; if (b != NULL) p->next = b; return head->next; } //打印单链表数据 void show_linklist(Node* head_node) { while (head_node != NULL) { std::cout << "data:" << head_node->data << std::endl; head_node = head_node->next; } } int main() { Node *head, *p, *q; int n = 5; //std::cin >> n; head = init_tailcreate(n); q = head; std::cout << "ori::" << std::endl; show_linklist(q); q = insert_data(head, 3, 10); std::cout << "insert::" << std::endl; show_linklist(q); q = delete_data(head, 3); std::cout << "delete::" << std::endl; show_linklist(q); std::cout << std::endl; q = delete_data_intail(head, 3); std::cout << "delete::" << std::endl; show_linklist(q); std::cout << std::endl; Node *head_a = init_createa(5); show_linklist(head_a); std::cout << std::endl; Node *head_b = init_createb(7); show_linklist(head_b); std::cout << std::endl; Node *merge_head = merge_sort_list(head_a, head_b); std::cout << "merge linklist:" << std::endl; show_linklist(merge_head); return 0; }
链表判断是否为回文串(需用到反转链表,递归反转链表、迭代反转链表):
#include <iostream> #include <stdio.h> #include <stdlib.h> char word_array[] = {'a', 'b', 'a', 'b', 'b', 'a', 'b', 'a'}; //回文字符串 //定义结构体 typedef struct LinkList { char data; LinkList *next; }pNode; //初始化回文链表 pNode* init_list(int n) { pNode *head, *p, *q; head = (pNode*)malloc(sizeof(pNode)); head->data = NULL; head->next = NULL; for (int i = 0; i < n; i++) { p = (pNode *)malloc(sizeof(pNode)); p->data = word_array[i]; p->next = NULL; if (head->next == NULL) { head->next = p; } else { q->next = p; } q = p; } return head; } //显示链表数据 void show_data(pNode* head) { pNode *curhead; curhead = head; while (curhead != NULL) { std::cout << "data:" << curhead->data << std::endl; curhead = curhead->next; } } //递归反转链表 pNode* reverse_list(pNode* p_head) { pNode *head; if (p_head->next == NULL || p_head == NULL) return p_head; head = reverse_list(p_head->next); p_head->next->next = p_head; p_head->next = NULL; return head; } //迭代反转链表 pNode* reverse_list_by_iteratino(pNode* p_head) { pNode *head, *next_node, *tem_node; if (p_head->next == NULL || p_head == NULL) return p_head; head = NULL; next_node = p_head; while (next_node) { tem_node = next_node->next; next_node->next = head; head = next_node; next_node = tem_node; } return head; } int main(){ pNode *head = NULL, *q, *p; head = (pNode*)malloc(sizeof(pNode)); head->data = NULL; head->next = NULL; int n = sizeof(word_array); //求字符串长度 int mid_node_index = 0; mid_node_index = n / 2; //找中点位置 int tem = mid_node_index; head = init_list(n); q = head->next; p = head->next; if (n == 1) { std::cout << "true." << std::endl; } else { if (n % 2 == 0 ) { while (tem--) { q = q->next; } q = reverse_list(q); //递归反转链表 // q = reverse_list_by_iteratino(q); //迭代反转链表 } else { while (tem--) { q = q->next; } q = q->next; q = reverse_list(q); // q = reverse_list_by_iteratino(q); } bool flag = true; for (int i = 0; i < mid_node_index; i++) { if (p->data != q->data) { flag = false; std::cout << "false." << std::endl; break; } p = p->next; q = q->next; } if (flag == true) std::cout << "true." << std::endl; } return 0; }
快慢指针判断链表是否有环、寻找链表中间结点。
思想:快指针每次走两步,慢指针每次走一步。当链表中有环时,快指针先进环,慢指针后进,慢指针没在环中走完一圈时,快指针一定能追上慢指针。
找中间节点时,快指针走完整个链表,慢指针只走了一半,此时慢指针指向的结点即为中间结点。
//判断链表是否有环 bool check_circle(pNode *p_head) { pNode *slow_node = p_head, *fast_node = p_head; if (p_head == NULL || p_head->next == NULL) return false; while (fast_node->next != NULL && fast_node->next->next != NULL) { slow_node = slow_node->next; fast_node = fast_node->next->next; if(slow_node == fast_node) { return true; } } return false; } //寻找链表中间结点 pNode* middle_node(pNode* p_head) { pNode *slow_node = p_head, *fast_node = p_head; if(p_head == NULL || p_head->next == NULL) return p_head; while(fast_node->next != NULL && fast_node->next->next != NULL) { fast_node = fast_node->next->next; slow_node = slow_node->next; } return slow_node; }
int main(){ pNode *head = NULL, *q, *p; head = (pNode*)malloc(sizeof(pNode)); head->data = NULL; head->next = NULL; head = init_list(n); pNode *circle_head, *c1, *c2; // 创建一个带环链表 circle_head->a->B->a circle_head->data = NULL; circle_head->next = NULL; c1 = (pNode*)malloc(sizeof(pNode)); c1->data = 'a'; c1->next = NULL; if (circle_head->next == NULL) circle_head->next = c1; c2 = c1; c1 = (pNode*)malloc(sizeof(pNode)); c1->data = 'B'; c1->next = NULL; c2->next = c1; c2 = c1; c2->next = circle_head->next; std::cout << check_circle(circle_head) << std::endl; //判断是否有环,有1,否0; pNode *temm = middle_node(head->next); //返回链表中间结点。 return 0; }