顺序表(整体代码)

#include<iostream>
#include<time.h>using namespace std;//顺序表的结构定义
typedef struct SqList
{int count;//顺序表存储了多少个元素int size;//顺序表的大小int* data;//顺序表的存储区
}SqList;//初始化顺序表
SqList* GetNewList(int n)
{SqList* p = (SqList*)malloc(sizeof(SqList));//指针大小p->size = n;p->count = 0;p->data = (int*)malloc(sizeof(int) * n);return p;//返回指向这个顺序表的指针
}//销毁顺序表
void clear(SqList* L)
{if (L == NULL){return;}free(L->data);free(L);return;
}//扩容操作
int expand(SqList* L)
{if (L == NULL)return 0;cout << "发生了扩容操作" << endl;//L->data = (int*)realloc(L->data, sizeof(int)*1.5*L->size);//扩容到原来的1.5倍,这种做法可能有bugint* p = (int*)realloc(L->data, sizeof(int) * 1.5 * L->size);if (NULL == p){cout << "扩容失败" << endl;return 0;}L->data = p;//规避bugL->size *= 1.5;return 1;
}//插入操作,插入到pos位置上
int insert(SqList* L, int pos, int val)
{if (pos < 0 || pos > L->size){cout << "插入位置不合法" << endl;return 0;}if (pos > L->count){cout << "插入失败,请按顺序插入" << endl;return 0;}if (L->size == L->count && !expand(L)){cout << "存储空间已满,无法插入" << endl;return 0;}for (int i = L->count - 1; i >= pos; i--){L->data[i + 1] = L->data[i];}L->data[pos] = val;L->count += 1;return 1;
}//删除操作
int erase(SqList* L, int pos)
{if (pos<0 || pos>L->count){cout << "删除位置不合法" << endl;return 0;}if (L->count == 0){cout << "存储空间为空,无法删除" << endl;return 0;}for (int i = pos; i < L->count - 1; i++){L->data[i] = L->data[i + 1];}L->count -= 1;return 1;
}//输出操作
void cout_List(SqList* L)
{int len = 0; // 用来记录输出的长度,为输出的格式服务for (int i = 0; i < L->size; i++){len += printf("%3d", i);}printf("\n");for (int i = 0; i < len; i++){printf("-");}printf("\n");for (int i = 0; i < L->count; i++){printf("%3d", L->data[i]);}printf("\n");return;
}int main()
{srand(time(0));SqList* L = GetNewList(2);for (int i = 0; i < 10; i++){int op = rand() % 5;//定义一个随机数来决定是插入还是删除int val, pos = 0;switch (op){case 0:case 1:case 2:case 3:pos = rand() % (L->count + 2);//+2是为了触发非法操作val = rand() % 100;cout << "插入" << val << "到" << pos << "位置" << endl;insert(L, pos, val);break;case 4:pos = rand() % (L->count + 2);cout << "删除" << pos << "位置的数据" << endl;erase(L, pos);break;}cout_List(L);}clear(L);return 0;
}

运行结果(不唯一)

删除1位置的数据
删除位置不合法
  0  1
------

插入59到0位置
  0  1
------
 59
删除0位置的数据
  0  1
------

插入24到0位置
  0  1
------
 24
插入22到1位置
  0  1
------
 24 22
插入95到3位置
插入位置不合法
  0  1
------
 24 22
插入11到2位置
发生了扩容操作
  0  1  2
---------
 24 22 11
插入17到0位置
发生了扩容操作
  0  1  2  3
------------
 17 24 22 11
插入32到2位置
发生了扩容操作
  0  1  2  3  4  5
------------------
 17 24 32 22 11
插入53到5位置
  0  1  2  3  4  5
------------------
 17 24 32 22 11 53

插入操作:

//插入操作,插入到pos位置上
int insert(SqList* L, int pos, int val)
{if (pos < 0 || pos > L->size){cout << "插入位置不合法" << endl;return 0;}if (pos > L->count){cout << "插入失败,请按顺序插入" << endl;return 0;}if (L->size == L->count && !expand(L)){cout << "存储空间已满,无法插入" << endl;return 0;}for (int i = L->count - 1; i >= pos; i--){L->data[i + 1] = L->data[i];}L->data[pos] = val;L->count += 1;return 1;
}

删除操作:

//删除操作,删除pos位置上的数据
int erase(SqList* L, int pos)
{if (pos<0 || pos>L->count){cout << "删除位置不合法" << endl;return 0;}if (L->count == 0){cout << "存储空间为空,无法删除" << endl;return 0;}for (int i = pos; i < L->count - 1; i++){L->data[i] = L->data[i + 1];}L->count -= 1;return 1;
}

扩容操作:

realloc工作原理

realloc会试着原内存地址的基础上向后进行延展,打个比方:原来的内存大小是4个字节,由于这4个字节大小的内存在整个内存空间中只是一小段,那它后面可能有没被使用的空间,此时realloc的尝试就成功了,那么realloc的返回值与原地址是一样的;

如果后面没有足够的空间进行扩容,那么realloc会重新找一片新的足够扩容的空间,将原来空间中的内容拷贝到新空间里来,再将原空间销毁并返回新空间的地址。

又如果找不到足够用来扩容的空间,那么realloc就会返回NULL,并且原内存不动,此时会出现bug。

//扩容操作
int expand(SqList* L)
{if (L == NULL)return 0;cout << "发生了扩容操作" << endl;//L->data = (int*)realloc(L->data, sizeof(int)*1.5*L->size);//扩容到原来的1.5倍,这种做法可能有bugint* p = (int*)realloc(L->data, sizeof(int) * 1.5 * L->size);if (NULL == p){cout << "扩容失败" << endl;return 0;}L->data = p;//规避bugL->size *= 1.5;return 1;
}

链表(整体代码)

#include<iostream>
#include<time.h>using namespace std;
//无头链表//定义链表节点信息
typedef struct ListNode
{int data;//数据域struct ListNode* next;//指针域
}ListNode;//初始化链表节点信息
ListNode* GetListNode(int val)
{ListNode* p = (ListNode*)malloc(sizeof(ListNode));p->data = val;p->next = NULL;return p;
}//销毁链表节点
void clear(ListNode* head)
{if (NULL == head)  return;ListNode* q = NULL;//定义一个临时节点for (ListNode* p = head; p; p = q){q = p->next;//如果不将p的下一个节点信息传给q而直接free的话会造成内存泄露free(p);}
}//插入操作,插入pos前一位并返回新插入链表节点的首地址
ListNode* insert(ListNode* head, int pos, int val)
{int len = 0;for (ListNode* p = head; p; p = p->next){len += 1;}if (len < pos)  pos = len;//超出链表长度则插入最后一个位置ListNode* node = GetListNode(val);ListNode new_head, * p = &new_head;//定义一个虚拟头节点,并将这个虚拟头节点的地址传给指针pnew_head.next = head;/*定义一个虚拟头节点有个好处:如果待插入位置是1,p就向后走1个位置...这样的话,逻辑思路就更加清楚*/for (int i = 0; i < pos; i++){p = p->next;}node->next = p->next;p->next = node;return new_head.next;
}//删除操作,删除pos位置的值
ListNode* erase(ListNode* head, int pos)
{if (pos < 0) {cout << "删除位置不合法" << endl;return head;}if (head == NULL) {cout << "无节点可删除" << endl;return head;}if (pos == 0) {ListNode* new_head = head->next; // 新的头节点free(head); // 释放原头节点return new_head;}ListNode* current = head;int i = 0;while (i < pos - 1 && current->next != NULL) {current = current->next;i++;}if (i < pos - 1 || current->next == NULL) {cout << "删除位置不合法" << endl;return head;}ListNode* temp = current->next;current->next = temp->next;free(temp);return head;
}//打印操作
void cout_list(ListNode* head)
{int len = 0;for (ListNode* p = head; p; p = p->next){len += 1;}for (int i = 0; i < len; i++){printf("%-4d", i);cout << " ";}cout << endl;for (ListNode* p = head; p; p = p->next){printf("%-3d", p->data);if (p->next != NULL){cout << "->";}}cout << endl;
}//查找操作
int find(ListNode* head, int val)
{ListNode* p = head;int n = 0;//记录经历了多少个节点while (p){if (p->data == val){cout_list(head);int len = n * (3 + 2);//3是一个数据占3位,2是'->'的长度,len代表空格的数量for (int i = 0; i < len; i++){cout << " ";}cout << "^" << endl;for (int i = 0; i < len; i++){cout << " ";}cout << "|" << endl;return 1;}n += 1;p = p->next;}return 0;
}int main()
{srand(time(0));ListNode* head = NULL;for (int i = 0; i < 10; i++){int op = rand() % 5;//定义一个随机数来决定是插入还是删除int val, pos = 0;switch (op){case 0:case 1:case 2:case 3:pos = rand() % (i + 1);val = rand() % 100;cout << "插入" << val << "到" << pos << "位置" << endl;head = insert(head, pos, val);break;case 4:pos = rand() % (i + 1);cout << "删除" << pos << "位置的数据" << endl;head = erase(head, pos);break;}cout_list(head);}int n = 0;while (cin >> n && n >= 0){if (!find(head, n)){cout<<"查找失败"<<endl;}}clear(head);return 0;
}

插入操作:

(无头节点的链表)

//插入操作,插入pos前一位并返回新插入链表节点的首地址
ListNode* insert(ListNode* head, int pos, int val)
{int len = 0;for (ListNode* p = head; p; p = p->next){len += 1;}if (len < pos)  pos = len;//超出链表长度则插入最后一个位置//防止第一个节点改变原来的链表地址if (pos == 0){ListNode* p = GetListNode(val);p->next = head;return p;}ListNode* p = head;for (int i = 0; i < pos-1; i++){p = p->next;}ListNode* new_node = GetListNode(val);//以下两步不能写反,否则会丢失后续链表的信息,造成内存泄露new_node->next = p->next;p->next = new_node;return head;
}

(有头节点的链表)

//插入操作,插入pos前一位并返回新插入链表节点的首地址
ListNode* insert(ListNode* head, int pos, int val)
{int len = 0;for (ListNode* p = head; p; p = p->next){len += 1;}if (len < pos)  pos = len;//超出链表长度则插入最后一个位置ListNode* node = GetListNode(val);ListNode new_head, * p = &new_head;//定义一个虚拟头节点,并将这个虚拟头节点的地址传给指针pnew_head.next = head;/*定义一个虚拟头节点有个好处:如果待插入位置是1,p就向后走1个位置...这样的话,逻辑思路就更加清楚*/for (int i = 0; i < pos; i++){p = p->next;}node->next = p->next;p->next = node;return new_head.next;
}

删除操作:

//删除操作,删除pos位置的值
ListNode* erase(ListNode* head, int pos)
{if (pos < 0) {cout << "删除位置不合法" << endl;return head;}if (head == NULL) {cout << "无节点可删除" << endl;return head;}if (pos == 0) {ListNode* new_head = head->next; // 新的头节点free(head); // 释放原头节点return new_head;}ListNode* current = head;int i = 0;while (i < pos - 1 && current->next != NULL) {current = current->next;i++;}if (i < pos - 1 || current->next == NULL) {cout << "删除位置不合法" << endl;return head;}ListNode* temp = current->next;current->next = temp->next;free(temp);return head;
}

查找操作:

//查找操作
int find(ListNode* head, int val)
{ListNode* p = head;int n = 0;//记录经历了多少个节点while (p){if (p->data == val){cout_list(head);int len = n * (3 + 2);//3是一个数据占3位,2是'->'的长度,len代表空格的数量for (int i = 0; i < len; i++){cout << " ";}cout << "^" << endl;for (int i = 0; i < len; i++){cout << " ";}cout << "|" << endl;return 1;}n += 1;p = p->next;}return 0;
}

小练习

反转链表(Leetcode-206):

给你单链表的头节点head,请你反转链表,并返回反转后的链表。

(非递归做法)

 struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}};class Solution
{
public:ListNode* reverseList(ListNode* head) {ListNode* p = head;ListNode new_head, * q;//定义虚拟头节点new_head.next = NULL;//采用头插法while (p){q = p->next;//p就相当于一个标记p->next = new_head.next;new_head.next = p;p = q;}return new_head.next;}
};

(递归做法)

 struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}};class Solution
{
public:ListNode* reverseList(ListNode* head) {//原链表为空或者只有一个节点就不用处理if (head == NULL || head->next == NULL){return head;}//对于头节点以外部分的链表,反转后的尾节点就是头节点的下一个节点ListNode* tail = head->next;//处理头节点以外部分的链表,new_head就是反转链表的地址ListNode* new_head = reverseList(head->next);//处理头节点,尾插法head->next = tail->next;tail->next = head;return new_head;}
};

环形链表(Leetcode-141):

给你一个链表的头节点 head ,判断链表中是否有环。

 struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}};class Solution
{
public:/** 就像操场上跑圈,跑得快的一定会追上跑得慢的,除非没有环*/bool hasCycle(ListNode* head) {//快慢指针ListNode* p = head, * q = head;while (q && q->next){p = p->next;//p每次跑一步q = q->next;//q每次跑两步if (p == q)return true;}return false;}
};

快乐数(Leetcode-202):

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

这个变化过程就像是链表:

如果把1看作空地址NULL,那么问题就变成了:从链表的一个节点开始,究竟能不能到达空地址。这就和环形链表很像了:如果它不是快乐数,那么它就会形成一个环,无法到达空地址。所以采用“快慢指针”的做法。

class Solution
{
public:int getnext(int x){//y代表变化后的数字int d, y = 0;while (x){d = x % 10;//d代表个位数y += d * d;x /= 10;}return y;}bool isHappy(int n) {int p = n, q = n;while (p != 1){p = getnext(p);q = getnext(getnext(q));//形成了‘环’且没到达终点if (p == q && p != 1)return false;}return true;}
};

旋转链表(Leetcode-61):

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

 struct ListNode {int val;ListNode *next;ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode *next) : val(x), next(next) {}};class Solution
{
public:int getLength(ListNode* head){int n = 0;while (head){n += 1;head = head->next;}return n;}ListNode* rotateRight(ListNode* head, int k) {//由于k可能大于链表的长度,故对k预处理if (head == NULL) return head;int len = getLength(head);k %= len;if (k == 0)  return head;/*这道题的解题思路就是将倒数k个节点分割出来放到‘前面’去,那么如何确定分割点呢?设定两个指针p、q,q往后移k+1个位置(即p和q间隔k个位置),然后让q和p同时向后移动,当q=NULL时,p所在的位置就是分割点了。*/ListNode* q = head, * p = head;for (int i = 0; i <= k; i++){q = q->next;}while (q){p = p->next;q = q->next;}//分割q = p->next;p->next = NULL;//分割完成,此时q是第二部分链表的头指针,接下来要让第二部分链表的尾节点接上原链表的头节点p = q;//这句代码执行后,p也变成了第二部分链表的头指针了//拼接,这个循环执行完之后,p就指向了第二部分链表的尾节点while (p->next){p = p->next;}p->next = head;return q;}
};

删除链表倒数第N个节点(Leetcode-19):

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

struct ListNode
{int val;ListNode* next;ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode* next) : val(x), next(next) {}
};class Solution
{
public:/*这道题与上道题的思路很想:要想删除第n个节点,就要找到第倒数第n+1个节点* 如何找呢?* 双指针+等距移动法*/ListNode* removeNthFromEnd(ListNode* head, int n){//如果只有5个节点,又恰好是删除倒数第5个位置的节点,那么N+1就不存在,为了避免这种情况,最好引入一个虚拟头节点ListNode new_head, * p = &new_head, * q = p;new_head.next = head;//让q指针往后移动n+1位for (int i = 0; i <= n; i++){q = q->next;}//让p指向待删除节点的前一位while (q){p = p->next;q = q->next;}//删除节点p->next = p->next->next;return new_head.next;}
};

环形链表Ⅱ(Leetcode-142):

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

/*环形链表Ⅱ的数学思路拆解:
*初始化一个前提:快指针每次前进2步,满指针每次前进1步。
*假设慢指针走了a的距离到达环的入口,
那么快指针就走了2a的距离,
设环的长度为x(也就是整个链表的长度减去a的长度),
那么快指针在环中经过的距离就是a,
于是快指针走完整个环的剩余长度就是x-a,
由于每一次‘行动’快指针都比慢指针多走1步,
那么两指针如果想在环中相遇,那么需要‘行动’x-a次,
此时二者相遇的位置沿环方向距离环入口的长度为a,
巧妙的是:链表的头节点距离环入口的距离也是a
*此时,如果把快慢指针中的一个扔回头节点,
再让它们以相同的速度向后移动,
那么它们再次相交的位置就是环入口。
*/struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}};class Solution
{
public:ListNode* detectCycle(ListNode* head) {ListNode* p = head, * q = head;while (q && q->next){p = p->next;q = q->next->next;if (p == q)break;}//循环结束有两种情况:①链表没有环②二者相遇了//排除第一种if (q == NULL || q->next == NULL){return NULL;}p = head;//把p扔回头节点while (p != q){p = p->next;q = q->next;}return q;}
};

反转链表Ⅱ(Leetcode-92):

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

/*
* 原链表由于左区间不为1,规模减小为子1;当左区间为1,右区间不为1,缩小规模为子2再到子3。
* 此时到达边界条件
*/
struct ListNode
{int val;ListNode* next;ListNode() : val(0), next(nullptr) {}ListNode(int x) : val(x), next(nullptr) {}ListNode(int x, ListNode* next) : val(x), next(next) {}};
class Solution
{
public:ListNode* reverseBetween(ListNode* head, int left, int right) {//边界条件if (left == 1 && right == 1){return head;}//左区间不为1if (left != 1){head->next = reverseBetween(head->next, left - 1, right - 1);}//左区间为1,右区间不为1,此时也说明当前链表的头节点在反转区域内else{//记录反转完之后的尾节点ListNode* tail = head->next;//对剩下的部分进行反转ListNode* new_head = reverseBetween(head->next, left, right - 1);//将当前链表的头节点插入到记录的尾节点之后head->next = tail->next;tail->next = head;head = new_head;}return head;}
};

数据结构-顺序表、链表的相关操作以及相关练习题相关推荐

  1. C语言链表的转置算法,c语言编程集 数据结构 顺序表 点链表 数制转换 矩阵转置.doc...

    c语言编程集 数据结构 顺序表 点链表 数制转换 矩阵转置 #include "stdio.h" #include "malloc.h" /*typedef s ...

  2. python顺序表数组_数据结构 | 顺序表

    什么是数据结构? 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成. 简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中. 比如:列表.集合与字典等都 ...

  3. C语言/C++常见习题问答集锦[八十三]之数据结构顺序表(operand types are error: no match for “operator==“)

    C语言/C++常见习题问答集锦[八十三]之数据结构顺序表{operand types are error: no match for "operator=="} 程序之美 前言 主 ...

  4. 线性表→顺序表→链表 逐个击破

    一. 线性表 1. 前言 线性表,全名为线性存储结构.使用线性表存储数据的方式可以这样理解,即 " 把所有(一对一逻辑关系的)数据用一根线儿串起来,再存储到物理空间中 ".这根线有 ...

  5. 数据结构--顺序表的使用

    数据结构--顺序表的使 #include<iostream> #include<cstdio> #include<cstring> using namespace ...

  6. 数据结构——顺序表的合并

    数据结构--顺序表的合并 具体要求:写一个函数,其函数的功能是将非递增顺序表LA和LB合并到非递增顺序表LC中 数据结构-顺序表的操作之合并顺序表 一.顺序表的结构 首先要定义的是顺序表的结构体,只有 ...

  7. 数据结构-顺序表(动态分配存储空间)

    数据结构-顺序表(动态分配存储空间) (1)顺序表的结构定义: 结构型定义:(动态分配存储空间) /*** 动态分配存储空间*/ #define InitSize 100 //动态分配存储空间时,不限 ...

  8. Educoder头歌数据结构顺序表及其应用

    头歌实践平台答案educoder 数据结构-顺序表及其应用 第1关:顺序表的实现之查找功能 /***************************************************** ...

  9. 8.基本数据结构-顺序表和链表

    一.内存 - 计算机的作用:对数据进行存储和运算.首先我们需要知道我们目前使用的计算机都是二进制的计算机,就以为着计算机只可以存储和运算二进制的数据.例如下载好的一部电影,该电影可以存储到计算机中,计 ...

  10. 数据结构顺序表的查找_数据结构1|顺序表+链表

    数据结构学习笔记1 进度:静态分配顺序表+单链表 参考资料:b站 王道考研+小甲鱼 < 判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注最高项目.的阶数. 推导大O阶方法 ...

最新文章

  1. 《预训练周刊》第29期:Swin Transformer V2:扩大容量和分辨率、SimMIM:用于遮蔽图像建模的简单框架...
  2. WMS学习笔记:1.尝试加载WMS
  3. 网站开发用什么语言好_网站开发教程:企业如何用网站开启在线业务?
  4. 信号与系统 chapter1 常见信号及其变换
  5. 【HDU - 4006】The kth great number (优先队列,求第k大的数)
  6. 我手撸了一个划线翻译工具!
  7. 个人所得税计算及多人避税问题
  8. java的类加载器ClassLoader
  9. Oracle数据库中dml提交,奇怪,ORACLE的触发器的DML操作,没有COMMIT,居然也能真正的提交掉???...
  10. php小米官网,小米商城的首页
  11. php网站 视频马赛克,如何给视频加马赛克 菜鸟也能学会的视频加马赛克解决方案...
  12. js实现图片放大镜效果——简单方法
  13. CSS类名及常用属性总结
  14. 如何求矩阵的特征值和特征向量
  15. 手写一个Spring Boot Starter
  16. Julia 数据科学应用
  17. 面试3连炮:聊聊ES写入数据的原理?查询数据的原理?倒排索引了解吗?
  18. 用div和css制作网页,DIVCSS网页设计总结:有用的3个网页制作_css
  19. 回应:“MJ广东开校之我见”
  20. 敢问路在何方 路在脚下

热门文章

  1. 合同能源管理服务认证审核与获取条件
  2. Perl脚本打包为独立执行程序
  3. 比你聪明的人还比你勤奋
  4. 关闭XPU盘自动播放
  5. 微信统一下单签名错误php,微信支付统一下单签名错误
  6. 【html+css】袁进 渡一
  7. ERROR 1062 (23000) at line 26903: Duplicate entry 'onli????' for key 'xxxx_script_name'
  8. python自娱自乐的聊天机器人
  9. 标准化设备坐标(Normalized Device Coordinates, NDC)
  10. MATLAB-ART算法