复制代码
线性表:
一个线性表里面可以是任意的数据元素,但是同一个线性表里面数据应该是同类型的
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;
}