线性表:一个线性表里面可以是任意的数据元素,但是同一个线性表里面数据应该是同类型的1 存在一个/唯一被称为第一个节点的节点2 存在一个/唯一被称为最后一个节点的节点3 除了第一个以外,每一个元素都有一个前驱节点4 除了最后一个,每一个元素都有一个后继节点满足以上性质,这个表就被称为线性表数组就是一个线性表想实现线性表的保存,我们需要考虑下面的事情1 元素要保存2 元素与元素之间的序偶关系谁是前面的谁是后面的我们有两种实现方式1 顺序结构 --- 数组所有的元素都有保存元素与元素之间的关系也被保存第一个的后面比是第二个,第二个的后面必是第三个......底层处理就是指针的步长来表示他们的关系a 第一个a + 1第二个.....序偶关系就被 +1 / -1表示出来了顺序表的基本操作增 -> O(n)删 -> O(n)改 -> O(1)查 -> O(n),如果有序,我们的查就可以提高效率(二分法)顺序表平时在使用的时候没有多大的问题,但是在数据量大的由于顺序表是连续的内存结构,因此面领着开不出空间的问题顺序表必须往大了开 --- 顺序表开出来之后就不可改变没有办法做到,你过来一个元素我才给你增加一个内存要解决顺序表的问题,我们需要考虑1 数据你要存2 元素与元素之间的序偶关系 --- 不像顺序表一样的,我们通过前后关系+1 -1就搞定了 我必须要弄一个什么东西来表示我们的前后最简单的就是指针,只要我这个元素里面有一个什么东西指向了我的后面的这个元素我就将我后面的关系给解决了因此我们的结构就变成了一个结构体,这个结构体里面分为两个部分1 数据元素2 序偶关系 --- 前后指针(单向只有一个指针,双向才会有两个指针)typedef int MyDataType;struct node{MyDataType data;//数据元素 数据域//序偶关系是指向我的前面或者后面的//前面的这个元素跟我是不是一模一样 //既然一模一样 那么就是本类指针struct node * next;//这个是指向我后面的元素 指针域struct node * prev;//这个是指向我前面的元素};单向链表:只需要保存它的前驱或者它的后继一般都是喜欢从前面往后面的因此我只需要一个next指针指向它的后继节点即可struct node//节点类型{MyDataType data;//数据元素 数据域struct node * next;//这个是指向我后面的元素 指针域};增 删 改 查 这些是基础的操作,见代码练习:尾插法插入到链表while(p ->next){p = p ->next;}//循环完毕 p就是最后一个在现有代码上面,输入一个什么数据,在这个链表找,如果有这个数据则返回他是第几个元素没有返回-1 -> 查 int SLLNH_Find_x(SLLNH_Node *first,const MyDataType x){}在x的前面加入y,如果x不存在则将y加入在最后x有可能是第一个,如果是第一个,将y加入到x的前面,第一个变成了y那么first就会发生改变,因此要将first返回SLLNH_Node * SLLNH_Add_x_y(SLLNH_Node *first,const MyDataType x,const MyDataType y){}删除x对应的那个节点(第一个) 如果x为first,删除之后first发生改变因此需要返回first 删除的这个节点是在堆上面的,因此要free,切记!!!SLLNH_Node * SLLNH_Delete_x(SLLNH_Node *first,const MyDataType x){}练习:1 我希望你建立一个升序的链表(链表的插入排序)找到第一个比它大的元素,将这个节点插入在这个元素的前面没有找到就插入到最后2 冒泡排序前面的比后面大就弄到交换位置3 反序输出这个链表 作业:1 就地逆置这个链表1 已经讲了2 每次将链表的第一个节点拿出来,头插法插入到新的链表将新的链表的第一个节点返回2 归并两个有序的链表,归并完毕,新的链表依然有序3 判断一个链表里面有没有环环:一坨形成一个圈了,循环不到终点利用快慢指针,如果快指针一次走两步,慢指针一次走一步快指针跑到终点就没有环快指针追上慢指针了,那么就有环了kuaide = kuaide ->next;if(kuaide){kuaide = kuaide ->next;}else{printf(没环);}交作业的共享文件夹的地址:计算机地址栏输入 回车\\192.168.5.249如果需要密码:Administrators实现单链表的尾插的时候发现必须要循环才能找到这个链表的最后一个并且我想得到这个链表里面有多少个节点必须要循环一遍我现在急需一种办法来解决这个问题你加入一个节点,我就记录你多了一个节点我会保存你的链表的第一个节点和最后一个节点或者其他的一些什么信息......我们让这个链表带上这个头部信息就可以了带头节点的链表头节点是用于管理这个链表的,除了管理这个链表,它本身是和这个链表没有什么联系的当我拿到这个头节点之后我就可以知道这个链表里面的一些基础信息struct node{MyDataType data;//数据域struct node * next;//指针域};struct head //头节点{//链表的第一个节点struct node * first;//链表的最后一个节点struct node * last;//链表里面有多少个元素int num;//还有其他的什么信息急需往后面扩充};
后续我们做链表基本上都是做这种带头节点的对带头节点的单链表进行增删改查//双向链表 -> 由于要保存第一个也要保存最后,因此最好的是用带头节点的双向链表它可以找到自己的后面那个元素也可以找到它的前面的那个元素指针域就会多一个由于有两个指针,因此操作的时候需要考虑的问题就多了一个增加节点 删除节点都会要动他们的指针struct node{MyDataType data;//数据域struct node * prev;//指针域 指向它的前面struct node * next;//指针域 指向它的后面};struct head //头节点{//链表的第一个节点struct node * first;//链表的最后一个节点struct node * last;//链表里面有多少个元素int num;//还有其他的什么信息急需往后面扩充struct node * ptr;//遍历指针 c++里面俗称迭代器(封装起来的一个指针)};哈希表 -> 数组不重增删(O(n)),重查找(O(1))链表重增删(O(1)),不重查找(O(n))哈希表就是为了解决上面两个不足的地方希望它的增删是(O(1)),查找也是(O(1))哈希表给数组和链表在不足的地方进行了优化但是有哈希冲突的问题,我们可以使用顺序哈希法或者链式哈希法一般我喜欢用链式哈希法:就是一个个的很短的链表组成见代码示例
#include <stdio.h>//从left排到right
void FastSort(int * a,int left,int right)
{if(left >= right)return;//找基准点 一般我都喜欢在左边int temp = a[left];//left 到 right是一个序列,这个序列是不能变的 因此我需要弄两个变量来表示左边和右边int r = right;int l = left;while(l < r)//l往右边推 r往左边推 当l到达r的时候 一趟就完了{//由于你的基准点是在左边 因此第一趟就得从右边往左边找 右边是大的,因此在里面挑小的for(;r > l;r--){if(a[r] < temp){//找到一个小的 将小的丢到左边去a[l] = a[r];break;}}//左边往右边推 找大的for(;l < r;l++){if(a[l] > temp){//找到一个大的 将大的丢到右边去a[r] = a[l];break;}}}//l == r的时候退出//将基准点放到它的位置 a[l] = temp;//一趟弄完 temp的左边都比temp要小 temp的右边都比temp要大//这么以来就将我们的序列分为temp的左边和temp的右边 只有temp排好了//左右两边的问题跟刚刚没有排的时候一模一样FastSort(a,left,l - 1);FastSort(a,r + 1,right);
}void Print(int * a,int n)
{for(int i = 0;i < n;i++){printf("%d ",a[i]);}printf("\n");
}int main()
{int a[] = {342,543,564,564,567,43,2,56,567,342,564,54,687,654,675,342};int size = sizeof(a) / sizeof(a[0]);Print(a,size);FastSort(a,0,size - 1);Print(a,size);return 0;
}
练习: 判断链表2是不是链表1的子序列