一.单链表
1.基本概念
data域和next域;不一定是连续存储(链式存储);有无头节点
头节点不存放具体数据,表示单链表的头
2.代码实现
添加(创建头节点-在链表最后添加节点,创建链表,通过辅助变量遍历链表【将节点加入链表】)
插入(temp是添加位置的前一个节点;新的节点.next=temp.next;temp.next=新的节点;flag的标识)
修改(temp.no==newnode.no;flag进行标识)
删除(找到需要删除的这个节点的前一个节点;temp.next=temp.next.next;被删除的节点会被回收)
二.双链表
1.基本概念
可以向前向后查找,可以自我删除
pre【指向前一个节点】
2.代码实现
遍历(和单链表一样,只不过可以向前向后查找)
修改(和单链表一样,只是修改了节点类型)
添加(找到链表的最后节点,temp.next=newNode,newNode.pre=temp)
删除(直接找到需要删除的节点,temp.pre.next=temp.next;temp.next.pre=temp.pre)
三.约瑟夫(单向环形链表)
1.问题描述
n个人围坐,第k个人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的人又出列,以此类推,直到所有人出列,由此产生一个出队编号的序列。
2.代码解决
创建环形链表
1.先创建第一个节点,让first指向该节点,并形成环形;(first=node,first.setNext(first),current=first)
2.后面当我们每创建一个新节点,就把该节点加入已有的环形链表中(current.setNext(node);node.setNext(first);current=node)
遍历
1.辅助指针current指向first节点(cur=first)
2.通过while循环遍历,current.next==first结束(cur.getNext==first;cur=cur.getNext)
出队序列
1.创建helper,事先指向环形链表的最后节点(helper.getNext==first结束循环)
注:小孩报数前,先让first和helper移动k-1次
2.开始报数时,让first和helper同时移动m-1次
这时first指向的节点就是出圈节点
3.first=first.next;helper.next=first(回收出圈节点)
四.栈
1.基本概念
先入后出的有序列表;栈顶(变化)和栈底;出栈(pop)和入栈(push)
应用场景:子程序的调用,递归调用,表达式的转换,二叉树的遍历,深度优先
2.代码实现
1.top表示栈顶,初始化为-1
2.入栈:top++;stack[top]=data
3.出栈:int value=stack[top];top--
4.遍历:从栈顶开始显示数据
ps:定义栈的maxSize和数组/链表模拟栈;栈满top==maxSize-1;栈空top==-1;
3.栈实现计算器
1.通过一个index值(索引)来遍历表达式
注:substring(index,index+1).charAt(0)
2.数字入数栈
3 符号栈为空--》直接入栈
不为空--》当前的操作符优先级小于或等于栈中的操作符,就需要从数栈pop出两个数,从符号栈pop出一个符号,进行运算,结果入数栈,当前操作符入栈; 大于直接入栈
注:减法:次栈顶-栈顶
4.扫描完顺序地pop出数字和符号
特:处理多位数--》定义一个变量字符串,进行拼接

五.前缀 中缀 后缀表达式(逆波兰计算器)
1.基本概念
前缀(波兰式):运算符位于操作数之前;
1.从右往左扫描表达式看数字,再从左往右看符号
2.遇到运算符弹出栈顶两个数做计算,将结果入栈(栈顶-次栈顶)
3.重复操作
中缀:一般转换为后缀表达式
后缀:运算符位于操作数之后
1.从左向右扫描
2.次栈顶-栈顶
2.代码
(1)逆波兰计算器
输入为后缀表达式
1.先将表达式放入ArrayList中(split方法)
2.再将ArrayList传递给一个方法(数入栈,符pop两数进行运算)
(2)中缀转后缀
思路:
1.初始化两栈,从左向右扫描表达式
2.操作数压s2
3.运算符:
(1)s1为空或者栈顶为"(" -->直接入栈
(2)优先级比栈顶元素高,也将运算符压入s1
(3) 若优先级一样或小于,将s1栈顶的运算符弹出并压入s2,再次和新的栈顶元素比较
4.括号:
(1)”(“ 直接入栈
(2)”)“依次弹出s1栈顶的运算符并压入s2,直到遇到左括号为止,此时这一对括号被丢弃
5.扫描完毕将s1的运算符依次弹出并压入s2
6.依次弹出s2的元素并输出,结果的逆序得到后缀表达式
代码:
(1)因为直接对str进行操作不方便,因此 先将中缀表达式转成对应的list
符号直接进入list,多位数拼接
(2)输入中缀list,得到后缀list
(3)s2可以直接使用List<String>
(4)因为是添加到list s2,直接输出s2就是后缀表达式
六.递归
1.基本概念
方法自己调用自己
注意:执行一个方法就会建立一个新的保护空间;局部变量是独立的;引用类型会共享数据;递归必须向递归条件逼近;谁调用就将结果返回给谁
2.相关代码
(1)迷宫
思路:
//先创建一个二维数组,模拟迷宫
//使用1表示墙
//上下左右置为1
//i,j表示从哪里出发;
//能找到map[6][5]位置,表示找到通路
//为0表示没有走过,为1表示墙,为2表示通路可以走,为3表示该点已经走过但是走不通
//策略:下-》右-》上-》左;走不通,再回溯
//先假定该点可以走通,置为2,按策略走,走不通置为3
//小球走过并标识
最短路径:通过改变策略,比较集合大小
(2)八皇后(任意两个皇后都不能处于同一行,同一列或同一斜线上)
思路:
//第一个皇后放在第一行第一列
//第二个皇后放在第二行第一列,然后判断是否欧克,不行依次把所有列放完
//继续放第三个皇后,直到第8个皇后放在一个不冲突的位置
//当得到一个正确解,在栈回退到上一个栈时,就会开始回溯
//然后回头继续将第一个皇后放第二列
用一维数组解决,对应arr下标,表示第几行,值表示第几列
查看当我们放置第n个皇后时(第二个皇后,n=1),就去检测该皇后是否和前面已经摆放的皇后冲突
Math.abs(n-i)==Math.abs(array[n]-array[i])
七.排序算法
1.基本介绍(一组数据按指定顺序进行排列)
1)内部排序
加载到内部存储器中进行排序
eg.插入(直接插入、希尔),选择(简单选择、堆),交换(冒泡、快速),归并,基数
2)外部
借助外部存储进行排序
3)时间频度T(n)
一个算法中的语句执行次数
忽略常数项;忽略低此项;忽略系数(立方不能忽略)
2.时间复杂度
T(n)和f(n)是同数量级函数,T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度
常数阶;对数阶;线性阶;线性对数阶;平方阶
尽可能避免指数阶的出现
1)平均时间复杂度(所有输入实例等概率出现)
2)最坏时间复杂度(运行时间的界限)
ps:空间复杂度:该算法所耗费的存储空间;常常出现空间换时间
八.冒泡排序
1.基本思路
通过对待排序序列从前往后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换。
(1)一共进行数组大小-1次大的循环
(2)每一次排序的次数都在减少
(3)如果我们发现在某趟排序中一次交换都没有发生,可以提前结束冒泡排序
2.代码
//第一趟排序,就是将最大的数排在最后
//两个循环,O(n^2)
优化:
//标识变量,标识是否进行过交换
//重置变量
九.选择排序
1.基本思路
属于内部排序
每次找最小(大)值依次放到最前面
(1)选择排序一共有数组大小-1轮排序,
(2)每一轮排序又是一个循环
(3)先假定当前这个数是最小数
(4)然后和后面的数进行比较,如果有更小的数,就重新确定最小数,得到下标
(5)当遍历到数组末尾就得到本轮最小数和下标
(6) 交换
2.代码
假定minIndex;min为0;不是就重置;arr[minIndex]=arr[0]、arr[0]=min进行交换
两个循环
优化:
判断是否需要进行交换
速度比冒泡快
十.插入排序
1.基本思路
把n个待排序的元素看成是一个有序表(1)和一个无序表(n-1),排序过程中每次从无序表取出第一个元素,把它的排序码和有序表元素的进行比较,将它插入有序表的适当位置
2.代码
//定义待插入的数和索引
//给insertVal找到插入位置(不越界;大小比较;需要的话将arr[insertIndex后移)
//退出循环,插入的位置:insertIndex+1
优化:
如果insertIndex+1==i就不需要赋值
十一.希尔排序
1.基本思路
把记录按下标的一定增量分组,对每组使用直接增量排序(进行交换);当增量减少为1,算法终止
2.代码
(1)交换式
//增量gap
//遍历各组中元素,步长
//和加上步长的元素进行比较,需要就交换
(2)移位式
//增量gap
//从第gap个元素开始,对其所在的组进行直接插入排序
十二.快速排序
1.基本原理
先将需要排序的数据分割成两部分(找基准),其中一部分的所有数据都比另外一部分的数据要小(设置两个索引),再按此方法对这两部分数据分布进行快速排序,整个排序过程递归进行
2.代码实现
左右索引(下标)(初始化),中轴
在中轴两边左右找,直到出现大于(小于)的数才退出{左右索引一直移动}
!防止死循环:arr[l]==pivot值时需要前移左指针;arr[i]==pivot值时需要后移右指针
!防止栈溢出:l==r时,l++,r--
分别向左,向右递归
十三.归并排序
1.基本原理
分而治之
合并:原始数组,索引(left,mid,right),中转数组
2.代码实现
//分+合方法
向左/右递归进行分解(mid)
合并
//合并
初始化左右序列的初始索引,以及中转数组的索引
(一)
先把左右两边(有序)的数据按照规则填充到temp数组
直到左右两边的有序序列,有一边处理完毕为止
(二)
把有剩余数据的一边的数据依次全部填充到temp
判断左右序列哪一边剩余
(三)
将temp数组的元素拷贝到arr
十四.基数排序
1.基本思路
将所有待比较数值统一为同样的数位长度,数位较短的前补0。然后从最低位进行排序。
一直从最低位排序一直到最高位排序。
(1)按照个/十/百/...位数的值放在对应的数组
(2)按照数组的下标顺序取出数据放入原来数组
空间换时间
基数排序是稳定的
2.代码实现
//得到数组最大的数的位数(max+"").length()
//定义一个二维数组:二维数组包含10个一维数组,为了防止数据溢出,每个一维数组大小定义为arr.length
//定义一个一维数组记录每个桶的每次放入的数据个数
//桶中有数据才放入原数组(index++)
//清零:每次取完后都需将一维数组清零
3.各排序速度比较

十五.二叉排序树
1.基本概念
对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。相同尽量避免。
2.代码
创建和遍历:
//创建节点;递归的方法添加结点和中序遍历
//root为空直接将root指向node
3.删除节点
删除:
(1)删除叶子节点
先找到需要删除的节点;targetNode
再找到target Node的父节点;parent
确定左子节点(parent.left=null)还是右子节点(parent.right=null);
//小(不小)于向左(右)子树递归查找,左(右)子节点为空直接返回null
//父节点:this.left!=null&&this.left.value==value;小于看左(右)子节点递归查找;不满足条返回null
//删除节点:二叉排序树只有一个节点直接root=null;确定targetNode是叶子节点
(2)删除只有一颗子树的节点
targetNode&&parent
确定targetNode的子节点是左子节点还是右
确定targetNode是左子节点还是右
左左:parent.left=targetNode.left
左右:parent.right=targetNode.left
右右:parent.right=targetNode.right
右左:parent.left=targetNode.right
//需要判断parent是否为空;为空需要root=targetNode.right(left)
(3)删除有两棵子树的节点
targetNode&parent
从targetNode的右(左)子树找最小(大)节点;用临时变量temp存储最小节点的值;删除该最小节点;targetNode.value=temp
//将targetNode.right传入,循环地查找右子节点
//targetNode.left&right!=null
4.平衡二叉树
左子树全部为空时查询速度很慢
是一颗空树或者左右两个子树的高度差不超过1
十六.图
1.基本介绍
处理多对多的关系
顶点,边,路径,无向图,有向图,带权图
表示方法:二维数组(邻接矩阵),数组+链表(邻接表)
2.代码
(1)创建
思路:存储顶点(ArrayList);存储边(二维数组)
添加边:v1,v2,(第一、二个点的下标),weight(0 or 1)
(2)深度优先遍历
每次都在访问当前节点后首先访问当前节点的第一个邻接节点
//访问后需将节点标记为已访问;深度优先遍历递归
//定义数组记录每个节点是否被访问
//查找i的第一个邻接节点w;存在-》未被访问-》递归;存在-》已被访问-》访问邻接节点的下一个邻接节点;对dfs进行重载,遍历所有节点进行dfs(针对非联通图)
(3)广度优先遍历
使用队列保持访问过的结点的顺序,按这个顺序来访问这些节点的邻接节点
//访问后需要进行标记并将节点加入队列;队列记录节点访问顺序;得到队列的头节点下标u,找第一个邻接节点w,以u为前驱点找w后面的下一个邻接点
//遍历所有节点进行广度优先
(4)dfs vs bfs
十七.动态规划
1.基本介绍
将大问题划分为小问题,与分治法不同,动态规划的子问题往往不是互相独立的,下一个子问题依赖于上一个子问题


v[i][0]=v[0][j] //表示填入表第一行和第一列是0
当w[i]>j时:v[i][j]=v[i-1][j]//当准备加入的商品的容量大于当前背包的容量时,就直接使用上一个单元格的装入策略
当j>=w[i]时:v[i][j]=max{v[i-1][j], v[i-1][j-w[i] ]+v[i] }//当准备加入的新增的商品的容量小于等于当前背包的容量,装入的策略:
v[i-1][j]:上一个单元格的装入的最大值
v[i]:表示当前商品的价值
v[i-1][j-w[i]]:装入i-1商品,到剩余空间j-w[i]的最大值
2.代码
十八.暴力匹配和KMP算法
1.基本介绍
字符串匹配问题(判断str1是否含有str2)
暴力匹配:
(1)匹配成功(str1[i]==str2[j]时,则i++,j++,继续匹配下一个字符
(2)失败:令i=i-(j-1),j=0;i回溯到第一个匹配成功的字符的下一位,j置零
如果j==str2.length,匹配成功,return i-j
KMP算法:
(1)利用已知信息,不要把“搜索位置”移回已经比较过的位置,继续把它向后移;
(2)搜索词,部分匹配值
(3)移动位数=已匹配的字符数-对应的部分匹配值
部分匹配值:“前缀”和“后缀”的最长的共有元素的长度
2.代码
//得到字符串的部分匹配值表
字符串长度为1,部分匹配值为0;dest.charAt(i)==dest.charAt(j)时,部分匹配值+1 ;dest.charAt(i)!=charAt(j)时,从next[j-1]获取新的j(回溯)
十九.贪心算法
1.基本介绍
集合覆盖问题:

贪心算法是指在对问题进行求解时,在每一步选择中都采取最好或者最优的选择。

(1)遍历所有的广播电台,找到一个覆盖了最多未覆盖地区的电台(此电台可以包含一些已覆盖地区)
(2)将这个电台加入集合中,想办法把该电台覆盖的地区在下次比较时去掉
(3)重复第一步直到覆盖所有地区
2.代码
//将广播电台放入HashMap中;allAreas存放所有地区;arraylist存放选择的电台集合;定义临时集合temp存放遍历过程中取出的电台覆盖的地区和当前还没有覆盖的地区的交集;maxKey保存能够覆盖最多地区的key
//如果当前集合包含的未覆盖地区的数量,比maxKey指向的集合地区还多,就需要重置key
//每次都需要置空maxKey,tempSet
1946

被折叠的 条评论
为什么被折叠?



