文章目录
前言
数据结构与算法学习笔记(一)
一、单链表
1.单链表的增删改查
class StudentNode{
int id;
String name;
String age;
String sex;
StudentNode next;
public StudentNode(int id,String name, String age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.id=id;
}
@Override
public String toString() {
return "StudentNode{" +
"id=" + id +
", name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
//链表添加
public void add(StudentNode studentNode){
StudentNode temp=head;
//找到链表最后一个元素
while (true){
if(temp.next==null){
break;
}
//指针后移
temp=temp.next;
}
//把元素添加到链表的最后
temp.next=studentNode;
}
//删除节点
public void delete(int id){
StudentNode temp=head;
boolean flag=false;
while(true){
//找到节点
if(temp.next.id==id){
flag=true;
break;
}
if(temp==null){
break;
}
temp=temp.next;
}
//将temp的下一个节点赋值为下下个节点 利用垃圾回收机制删除节点
if(flag){
temp.next=temp.next.next;
}else {
System.out.println("没有找到节点,节点已经删除");
}
}
//根据id修改链表
public void update(StudentNode studentNode){
StudentNode temp=head.next;
if(temp==null){
System.out.println("链表为空");
return;
}
//是否找到节点
boolean flag=false;
while (true){
if(temp==null){
break;
}
if(temp.id==studentNode.id){
flag=true;
break;
}
temp=temp.next;
}
if(flag){
temp.age=studentNode.age;
temp.name=studentNode.name;
temp.sex=studentNode.sex;
}
System.out.println("没找到相同Id"+studentNode);
}
//根据Id查询链表
public void searchById(int id){
StudentNode temp=head.next;
boolean flag=false;
if(temp==null){
System.out.println("链表是空的");
}
while(true){
if(temp==null){
break;
}
if(temp.id==id){
flag=true;
break;
}
temp=temp.next;
}
if(flag){
System.out.println(temp);
}else{
System.out.println("没找到该元素");
}
}
//根据Id顺序添加
public void addById(StudentNode studentNode){
StudentNode temp=head;
//判断Id是否存在
boolean flag=false;
while(true){
if(temp.next==null){
break;
}
if(studentNode.id==temp.id){
flag=true;
break;
}
if(studentNode.id<temp.next.id){
break;
}
temp=temp.next;
}
if(flag){
System.out.println("添加学生Id已存在"+studentNode);
}
studentNode.next=temp.next;
temp.next=studentNode;
}
2.单链表遍历,反转
//链表遍历
public void search(){
StudentNode temp=head.next;
if(head.next==null){
System.out.println("链表是空的");
return;
}
while(true){
if(temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
//单链表反转
public static void fzLinkedList(StudentNode head){
//如果是空链表,或者链表只有一个元素 则不用反转
if(head.next==null||head.next.next==null){
return;
}
StudentNode newhead=new StudentNode(0,"","","");
StudentNode cur = head.next;
StudentNode next=null;
while(cur!=null){
//保存cur后面的元素
next=cur.next;
//把新头节点后面的元素放到遍历元素的后面
cur.next=newhead.next;
//把遍历出来的元素添加到新的头节点后面
newhead.next=cur;
//指针后移
cur=next;
}
//将反转后的数据接到head后面
head.next=newhead.next;
}
二、双向链表
1.双链表增删改查
class StudentNode2 {
int id;
String name;
String age;
String sex;
StudentNode2 next;
StudentNode2 pre;
public StudentNode2(int id, String name, String age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
}
@Override
public String toString() {
return "StudentNode{" +
"id=" + id +
", name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
//链表添加
public void add(StudentNode2 studentNode){
StudentNode2 temp=head;
//找到链表最后一个元素
while (true){
if(temp.next==null){
break;
}
//指针后移
temp=temp.next;
}
//把元素添加到链表的最后并且让元素的pre指向前一个元素
temp.next=studentNode;
studentNode.pre=temp;
end=studentNode;
}
//删除节点
public void delete(int id){
StudentNode2 temp=head.next;
boolean flag=false;
while(true){
//找到节点
if(temp.id==id){
flag=true;
break;
}
if(temp==null){
break;
}
temp=temp.next;
}
//将temp的下一个节点赋值为下下个节点 利用垃圾回收机制删除节点
if(flag){
temp.pre.next=temp.next;
//防止Temp是最后一个元素出现空指针异常
if(temp.next!=null){
temp.next.pre=temp.pre;
}else{
//如果是最后一个元素 把end前移
end=temp.pre;
}
}else {
System.out.println("没有找到节点,节点已经删除");
}
}
//根据id修改链表
public void update(StudentNode2 studentNode){
StudentNode2 temp=head.next;
if(temp==null){
System.out.println("链表为空");
return;
}
//是否找到节点
boolean flag=false;
while (true){
if(temp==null){
break;
}
if(temp.id==studentNode.id){
flag=true;
break;
}
temp=temp.next;
}
if(flag){
temp.age=studentNode.age;
temp.name=studentNode.name;
temp.sex=studentNode.sex;
}
System.out.println("没找到相同Id"+studentNode);
}
//链表遍历
public void search(){
StudentNode2 temp=head.next;
if(head.next==null){
System.out.println("链表是空的");
return;
}
while(true){
if(temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
//逆序遍历
public void nixuSearch(){
StudentNode2 temp=end;
while(true){
if(temp==head){
break;
}
System.out.println(temp);
temp=temp.pre;
}
}
三、环形链表
1.循环链表添加和遍历
//环形链表
public void addHuan(StudentNode studentNode){
if(first==null){
first=studentNode;
studentNode.next=first;
}
StudentNode temp=first;
while(true){
if(temp.next==first){
break;
}
temp=temp.next;
}
temp.next=studentNode;
studentNode.next=first;
}
public void searchHuan(){
StudentNode temp=first;
if(first==null){
System.out.println("链表为空");
}
while(true){
if(temp.next==first){
System.out.println(temp.id);
break;
}
System.out.println(temp.id);
temp=temp.next;
}
}
2.约瑟夫问题
//约瑟夫问题
/**
*
* @param startNo 从第几个开始数
* @param count 数几次
* @param nums 开始有几个
*/
public void countStudent(int startNo,int count,int nums){
if(first==null||startNo<1||startNo>nums){
System.out.println("输入参数不对");
return;
}
StudentNode temp=first;
//先定位到最后一个元素
while (true){
if(temp.next==first){
break;
}
temp=temp.next;
}
//将最后一个元素和第一个元素移动stratNo
for (int i = 0; i <startNo-1 ; i++) {
first=first.next;
temp=temp.next;
}
while (true){
if(temp==first){
break;
}
//每移动count-1输出id
for (int i = 0; i <count-1 ; i++) {
first=first.next;
temp=temp.next;
}
System.out.println(first.id);
first=first.next;
temp.next=first;
}
System.out.println("最后一个是"+first.id);
}
public void addyue(int nums){
if(nums<0){
System.out.println("添加的数量有误");
return;
}
StudentNode curStudent = null;
for (int i = 1; i <= nums; i++) {
StudentNode studentNode=new StudentNode(i);
if(i==1){
first=studentNode;
first.next=first;
curStudent=first;
}else{
curStudent.next=studentNode;
studentNode.next=first;
curStudent=studentNode;
}
}
}
四、栈
1.栈的压栈弹栈遍历
public class Stack {
int top=-1;
int maxSize;
int stack[];
public Stack(int maxSize) {
this.stack = new int[maxSize];
}
public boolean isFull(){
return top==maxSize-1;
}
public boolean isEmpty(){
return top==-1;
}
//压栈
public void push(int node){
if(isFull()){
System.out.println("栈已满");
}
top++;
stack[top]=node;
}
//弹栈
public int pop(){
if(isEmpty()){
throw new RuntimeException("栈空");
}
int node=stack[top];
top--;
return node;
}
//遍历栈
public void search(){
if(isEmpty()){
System.out.println("栈空");
}
for (int i = top; i >=0 ; i--) {
System.out.println(stack[i]);
}
}
}
2.栈实现计算器
public class Computer {
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
String str=s.next();
Stack2 opers=new Stack2(10);
Stack2 nums=new Stack2(10);
int num1=0;
int num2=0;
char oper=0;
int result=0;
char[] a=str.toCharArray();
for (int i = 0; i <a.length; i++) {
//判断是不是符号
if(Stack2.isOper(a[i])){
//符号栈不为空就比较当前符号与栈顶符号,如果当前符号优先级小于栈顶符号就先计算栈顶符号
//从数字栈中弹出两个,从符号栈中弹出一个进行计算兵将结果加入数字栈.最后将符号入栈
if(!opers.isEmpty()){
if(opers.fuHao(a[i])<=opers.fuHao((char) opers.getTop())){
num1=nums.pop();
num2=nums.pop();
oper= (char) opers.pop();
result=nums.jiSuan(num1,num2,oper);
nums.push(result);
opers.push(a[i]);
}else{
opers.push(a[i]);
}
}else{
//是空入栈
opers.push(a[i]);
}
}else{
nums.push(a[i]-48);
}
}
//过滤完毕后符号栈就只剩+-进行计算即可.
while (true){
if(opers.isEmpty()){
break;
}
num1=nums.pop();
num2=nums.pop();
oper= (char) opers.pop();
result=nums.jiSuan(num1,num2,oper);
nums.push(result);
}
System.out.println(nums.pop());
}
}
//计算
public int jiSuan(int num1,int num2,char oper){
int result = 0;
switch (oper){
case '+' :
result=num1+num2;
break;
case '-' :
result=num2-num1;
break;
case '*' :
result=num1*num2;
break;
case '/' :
result=num2/num1;
break;
default:
break;
}
return result;
}
//判断符号优先级
public int fuHao(char oper){
if(oper=='*'||oper=='/'){
return 1;
}else if (oper=='+'||oper=='-'){
return 0;
}else{
return -1;
}
}
五、递归
1.迷宫回溯
//利用递归栈执行原理 第一个if不断往后走,上下左右都走不通的话,根据栈的执行回溯到上一个if继续执行,通过
不断回溯来找到出口的路
public static boolean miGong(int[][] map, int i,int j) {
//找到出口 6 5
if(map[6][5]==2){
return true;
}else {
//如果该点是0说明还没有走过
if(map[i][j]==0){
map[i][j]=2;
//先向右走
if(miGong(map,i,j+1)){
return true;
}else if(miGong(map,i+1,j)){
//向下走
return true;
}else if(miGong(map,i,j-1)){
//走不通向左走
return true;
}else if(miGong(map, i-1, j)){
//最后向上走
return true;
}else{
//都走不通
map[i][j]=3;
return false;
}
}else{
return false;
}
}
}
}
//测试
public static void main(String[] args) {
int[][] map=new int[8][7];
for (int i = 0; i <=map.length-1; i++) {
for (int j = 0; j <map[i].length ; j++) {
map[i][j]=0;
}
}
for (int i = 0; i <=map.length-1 ; i++) {
map[i][0]=1;
map[i][6]=1;
}
for (int i = 0; i <7 ; i++) {
map[0][i]=1;
map[7][i]=1;
}
map[3][1]=1;
map[3][2]=1;
miGong(map,1,1);
for (int i = 0; i <map.length; i++) {
for (int j = 0; j <map[i].length ; j++) {
System.out.print(map[i][j]+" ");
}
System.out.println();
}
}
2.八皇后问题
/*通过不断回溯一个一个试出来的,到了第八层以后未成功会在第8层数组一个个试直到return,
*return是又返回到第七层,第七层接着往下遍历.如果第七层满足条件又会调用添加棋子的方法进行
*添加.同理第七层return后第六层也一样直到return到第一层结束.递归结束,
*利用压栈的执行原理,将所有结果都试出来.
*/
public class Queue8 {
int max=8;
int[] array=new int[max];
public static void main(String[] args) {
Queue8 q=new Queue8();
q.queue(0);
}
//放置棋子
public void queue(int n){
//n等于8路径成功
if(n==max){
print();
return;
}
//不等于8说明未成功 让它在第n层从0到max-1不断的试
for(int i=0;i<max;i++){
array[n]=i;
//依次判断是否冲突 不冲突说明n层放对了,接着放下一层
if(judge(n)){
queue(n+1);
}
}
}
//判断放置是否合理
public boolean judge(int n){
for(int i=0;i<n;i++){
//如果后面添加的元素和n在同一条直线或者同意条斜线上 根据数学知识,行和列的绝对值相等就是等腰三角形在同一条直线上
if(array[i]==array[n]||Math.abs(array[n]-array[i])==Math.abs(n-i)){
return false;
}
}
return true;
}
public void print(){
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
System.out.println();
}
}
六、排序算法
1.冒泡排序
public static int[] dubbleSort(int[] array){
int temp=0;
boolean flag=false;
for (int i = 0; i <array.length-1 ; i++) {
for (int j = 0; j <array.length-1-i ; j++) {
if (array[j] > array[j+1]){
flag=true;
temp=array[j+1];
array[j+1]=array[j];
array[j]=temp;
}
}
//如果没有发生交换 说明是有序的 直接结束
if(!flag){
break;
}else{
flag=false;
}
}
return array;
}
2.选择排序
public static int[] selectSort(int[] array){
for(int i=0;i<array.length-1;i++) {
int min=array[i];
int minindex=i;
//每一轮元素排一个,下一次从第i+1个开始比大小
for (int j = i+1; j <= array.length - 1 ; j++) {
if (array[j] < min) {
//通过for找最小值
min = array[j];
minindex = j;
}
}
//若最小值不是下标为i的数就交换
if(minindex != i){
array[minindex]=array[i];
array[i]=min;
}
}
return array;
}
3.插入排序
//注:没有break的插入都不是插入 可能是在有序数组后面加了一个数又进行冒泡排序去了.
//数组的第一个元素看作有序数组,从第二个元素开始插入.
public static int[] insertSort(int[] array){
int j=0;
//array[i]是有序数组和无序数组的分界点
for (int i = 1; i < array.length; i++) {
int temp = array[i];
for ( j = i; j>0 ; j--) {
//倒序遍历有序找大于分界点的值,找到结束.没找到遍历过的都后移
if( temp < array[j - 1]){
break;
}else{
array[j]=array[j-1];
}
}
//将分界点保存的值插入到找到元素的下一个位置
array[j]=temp;
}
return array;
}
//for+while
public static int[] insertSort(int[] array){
for (int i = 1; i < array.length; i++) {
int temp = array[i];
int j=i;
//找到大于temp的点
while(j>0&&array[j-1]>temp ){
array[j]=array[j-1];
j--;
}
array[j]=temp;
}
return array;
}
4.希尔排序
//交换法
public static int[] ShellSort(int[] array){
int temp=0;
//把数组分成length/2组,通过不断缩小分组,最后只有一组进行排序.
for (int i=array.length/2;i>0;i=i/2){
//将array[k]和array[k+i]分为一组 若k比k+i大交换位置 其实相当于冒泡的希尔排序
for(int j=i;j<array.length;j++){
for (int k = j-i; k >=0 ; k-=i) {
if(array[k]>array[k+i]){
temp=array[k];
array[k]=array[k+i];
array[k+i]=temp;
}
}
}
}
return array;
}
//移位法
public static int[] ShellSort2(int[] array){
for (int i=array.length/2;i>0;i=i/2){
for(int j=i;j<array.length;j++){
int k=j;
int temp=array[k];
if(array[k]<array[k-i]){
//如果同一组的后一个元素小于前一个元素 那么就找到大于array[k]的元素 然后插入.这个是希尔分组插入排序
while (k-i>=0 &&temp<array[k-i]){
array[k]=array[k-i];
k -= i;
}
array[k]=temp;
}
}
}
return array;
}
5.快速排序
/*
*快速排序是选定一个基准点 定义两个指针 从左到右和从右到左遍历,找到左边大于基准点
*和右边小于基准点的下标将他俩交换 继续找,知道两个指针相遇停止,相遇点就是基准点的位置
*将基准点和相遇位置进行交换 这样就通过基准点分割成两个数组,基准点左边是小于基准点的数组,
*基准点右边是大于基准点的数组.通过不断的递归最终使数组完成排序
*/
public static void quickSort(int[] array, int low, int high){
int temp;
int i,j,t;
if(low>high){
return;
}
i=low;
j=high;
//选最左边作为基准点;
temp=array[low];
//左边大于右边说明已经跑完
while(i<j){
while(i<j&&temp<=array[j]){
j--;
}
while (i<j&&temp>=array[i]){
i++;
}
//上面出来并且i<j 说明找到i和j即左边大于temp和右边小于temp的下标,将他们俩个换位
if(i<j){
t=array[j];
array[j]=array[i];
array[i]=t;
}
}
//最后让基准与i或j下标互换
array[low]=array[i];
array[i]=temp;
//基准左边调用递归
quickSort(array,low,j-1);
//基准右边调用递归
quickSort(array,j+1,high);
}
6.归并排序
/*
*归并排序先将数组分成一个个,利用递归结束后从后往前执行的原理,先分后治.完成排序
*合并的时候定义两个指针分别指向要合并的两个数组的最左边.比较两个大小,哪个大
*就把哪个加到临时数组中并且指针右移.一个数组全部加完后将剩下数组没加完的全部
*扔到临时数组中,即可完成两个数组的合并,通过递归的原理,使所有分开的数组都完成
*合并,最终完成数组排序
*/
public static void mergeSort(int[]array,int left,int right,int[]temp){
if(left<right){
int mid=(left+right)/2;
//左边递归分
mergeSort(array,left,mid,temp);
//右边分
mergeSort(array,mid+1,right,temp);
merge(array,left,mid,right,temp);
}
}
public static void merge(int[] array,int left,int mid,int right,int[]temp){
int i=left;
int j=mid+1;
int t=0;
//左边没结束或者右边没结束就一直排
while(i<=mid&&j<=right){
//哪边小就往数组里面存哪边
if(array[i]<=array[j]){
temp[t]=array[i];
t++;
i++;
} else{
temp[t]=array[j];
t++;
j++;
}
}
//左边没排完或者右边没排完直接加到数组后面
while(i<=mid){
temp[t]=array[i];
i++;
t++;
}
while(j<=right){
temp[t]=array[j];
j++;
t++;
}
t=0;
//排序后把temp数组种的拷贝到array种
int tempLeft=left;
while(tempLeft<=right){
array[tempLeft]=temp[t];
t++;
tempLeft++;
}
}
7.基数排序
/*
*典型的用空间换时间的排序.3000+W数据时内存已经就不够了.
*基数排序就是将个位数都取出来进行排序按照个位数的顺序放进数组,又将得到的数组按照十位数排序
*以此类推最终将最高位排完以后就是一个有序的数组.
*/
public static int[] baseSort(int[] array){
int max=array[0];
int[][] bucket=new int[10][array.length];
int[] element = new int[10];
//找出数组中的最大值
for(int i=0;i<=array.length-1;i++){
if(array[i]>max){
max=array[i];
}
}
int maxLength=(max+"").length();
//循环使各个进制都被计算
for(int l=0,n=1;l<maxLength;l++,n*=10){
for(int j=0;j<=array.length-1;j++){
//取出元素对应进制中的值
int digit=array[j] / n % 10;
//element[digit]是放置元素的个数 digit是对应进制的下标 将每个元素放置在他对应的桶中
bucket[digit][element[digit]]=array[j];
element[digit]++;
}
//将对应桶中的元素取出来放置到原来的数组中;遍历桶
int index=0;
for(int k=0;k<element.length;k++){
if(element[k]!=0){
//如果桶有数据
for (int i = 0; i <element[k] ; i++) {
array[index]=bucket[k][i];
index++;
}
}
//取完数据将桶内元素个数归零
element[k]=0;
}
}return array;
}
七、查找算法
1.二分查找
//递归法
public static int binarySearch(int[] array,int value,int left,int right){
if(value < arr[left] || value > arr[right] || left>right){
return -1;
}
int mid=(left+right)/2;
int minVal=array[mid];
//如果要查找的值大于中间值 那么向右递归
if(value>minVal){
return binarySearch(array,value,mid+1,right);
//如果小于中间值 向左递归
}else if(value<minVal){
return binarySearch(array,value,left,mid-1);
}else{
return mid;
}
}
//while循环
public static int binarySearch(int[] arr,int key){
int low = 0;
int high = arr.length - 1;
int mid = 0; //定义mid
//key大于最大小于最小 或者数组时空的 返回-1
if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
while(low <= high){
mid = (low + high) / 2;
if(arr[mid] > key){
//比关键字大则关键字在左区域
high = mid - 1;
}else if(arr[mid] < key){
//比关键字小则关键字在右区域
low = mid + 1;
}else{
return mid;
}
}
//最后仍然没有找到,则返回-1
return -1;
}
2.插值查找
/*
*插值查找其实就是把二分查找取中值变为自定义的值,更容易找到要查找的数
*/
public static int binarySearch(int[] array,int value,int left,int right){
if(left>right||value<array[left]||value>array[right]){
return -1;
}
int mid=left+(right-left)*(value-array[left])/(array[right]-array[left]);
int minVal=array[mid];
//如果要查找的值大于中间值 那么向右递归
if(value>minVal){
return binarySearch(array,value,mid+1,right);
}else if(value<minVal){
return binarySearch(array,value,left,mid-1);
}else{
return mid;
}
}