数据结构与算法

-------单链表

  1. 特点:不知道总个数。
package com.weichen.Singlelinkedlist;
import java.util.Stack;

//alt+shift+L   
public class Singlelinkedlist2 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub 
		//new 新节点
		HeroNode_new node1=new HeroNode_new(1, "bbb", "111");
		HeroNode_new node2=new HeroNode_new(2, "ccc", "222");
		HeroNode_new node3=new HeroNode_new(3, "ddd", "333");
		HeroNode_new node4=new HeroNode_new(4, "eee", "444");
		
		HeroNode_new node5=new HeroNode_new(5, "eee", "444");
		HeroNode_new node6=new HeroNode_new(6, "eee", "444");
		HeroNode_new node7=new HeroNode_new(7, "eee", "444");
		HeroNode_new node8=new HeroNode_new(8, "eee", "444");
		
		
		HeroNode_new head=new HeroNode_new(1, "bbb","bbb");
		HeroNode_new updateNode=new HeroNode_new(6, "bbb","-----");
		
		
		
		//new 新链表
		Linkedlist_new linkedlist=new Linkedlist_new();
		
//		//向链表中顺次添加节点
//		System.out.println("在链尾添加节点");
//		linkedlist.addLinkedlist(node4);
//		linkedlist.addLinkedlist(node2);
//		linkedlist.addLinkedlist(node3);
//		linkedlist.addLinkedlist(node1);
//		//显示链表---顺次
//		System.out.println("显示当前链表--顺次添加");
//		linkedlist.list();
		
		//向链表中按序添加节点
		System.out.println("按序添加节点");
		linkedlist.addOrderLinkedlist(node5);
		linkedlist.addOrderLinkedlist(node6);
		linkedlist.addOrderLinkedlist(node7);
		linkedlist.addOrderLinkedlist(node8);
		
		//显示链表--按序
		System.out.println("显示当前链表--按序添加");
		linkedlist.list();
		
		//链表反转    结果是打印出反转后的链表
		System.out.println("链表反转");
		linkedlist.reverse(linkedlist.gethHeadNode());
		//链表反转
		linkedlist.list();
//		
		System.out.println("链表二次反转");
		linkedlist.reverse(linkedlist.gethHeadNode());
		//链表反转
		linkedlist.list();
//		
		//反向打印链表
		System.out.println("反向打印链表");
		linkedlist.reverse_print(linkedlist.gethHeadNode());   //加上头节点为了指针复原
//		
//		
		//链表更改     要更改指定节点的信息,所以要先指定id
		System.out.println("更改指定元素");
		linkedlist.update(updateNode);
		linkedlist.list();
//		
//		
		//删除链表某一指定元素
		System.out.println("删除链表某一指定元素");
		linkedlist.delete(8);
		linkedlist.list();
	}
}
		

//链表
class Linkedlist_new{
	private HeroNode_new headNode=new HeroNode_new(0, "","");
	public HeroNode_new gethHeadNode() {
		return headNode;
	}
	
	//添加元素  传入一个节点
	public void addLinkedlist(HeroNode_new node4) {
		//定义临时指针
		HeroNode_new temp=headNode;
		//找要添加节点的位置
		while(true) {
			if(temp.next==null) {
				break;
			}else {
				temp=temp.next;
			}
		}
		//找到位置后放上新节点
		temp.next=node4;
	}

		//按序向链表添加元素
		public void addOrderLinkedlist(HeroNode_new b_node) {
			HeroNode_new temp=headNode;
			boolean flag=false;
			while(true) {
				if(temp.next==null) {    //直接在末尾插入即可
					temp.next=b_node;
					break;
				}
				if(temp.next.id==b_node.id) {
					System.out.println("重复插入");    //打印一句话退出循环即可
					break;
				}else if(temp.next.id>b_node.id) {     //找到位置
					flag=true;
					break;
				}else {
					temp=temp.next;     //未找到位置,指针后移
				}
			}
			if(flag) {
				b_node.next=temp.next;
				temp.next=b_node;
			}	
		}
		
		//遍历链表
		public void list() { 
			int count=0;      //用来统计链表中元素个数
			HeroNode_new temp=headNode.next;
			//遍历链表首先要判断链表是否为空
			if(headNode.next==null) {
				System.out.println("链表为空");
				return;
			}
			//遍历   若temp!==null则继续遍历    若为空,退出循环
			while(true) {
				if(temp!=null) {
					System.out.println(temp.toString());
					count++;
					temp=temp.next;
				}else {
					break;
				}
			}
		}
		
		//返回倒数第k个元素
		//首先考虑链表性质,不能提前知道链表元素的个数,所以首先遍历链表得到个数。找到元素位置
		public HeroNode_new heroNode_index(HeroNode_new headNode,int index) {
			int count=0;
			if(headNode.next==null) {
				return null;
			}
			HeroNode_new temp=headNode.next;
			while(true) {
				if(temp==null) {
					break;
				}else {	
					count++;
					temp=temp.next;
				}
			}
			
			//首先要考虑index是否在范围
			if(index<=0||index>count) {
				return null;
			}
			
			//找到需要返回元素的具体位置
			int n_index=count-index;
			for(int i=0;i<n_index;i++)
			{
				temp=temp.next;
			}
			return temp;
		}
		
		//更新节点信息    传入一个新节点    先找到节点id
		public void update(HeroNode_new updateNode) {
			//先判断链表是否为空
			if(headNode.next==null) {
				System.out.println("链表为空");
				return;
			}
			HeroNode_new temp=headNode.next;
			boolean flag=false;
			while(true) {
				if(temp==null) {
					break;
				}
				if(temp.id!=updateNode.id) {
					temp=temp.next;
				}else if(temp.id==updateNode.id){
					flag=true;
					break;
				}
			}
			if(flag){
				temp.name=updateNode.name;
				temp.nickname=updateNode.nickname;
			}
		}
		
		//删除指定元素
		public void delete(int id) {
			if(headNode.next==null) {
				System.out.println("链表为空");
				return;
			}
			HeroNode_new temp=headNode;
			boolean flag=false;
			
			//先找位置   temp.id=id
			while(true) {
				if(temp.next.id==id) {
					flag=true;
					break;
				}
				if(temp.next.id!=id) {
					temp=temp.next;
				}
				if(temp.next==null) {
					System.out.println("没有找到待删除节点");
					break;
				}
			}
			if(flag) {
				temp.next=temp.next.next;
			}
		}
			
		//反转链表
		public void reverse(HeroNode_new headNode) {
			if(headNode.next==null || headNode.next.next==null) {
				return;
			}
			HeroNode_new headnode2=new HeroNode_new(0, "","");  //指向第二个链表
			HeroNode_new temp1=headNode.next;
			HeroNode_new temp2=headnode2;
			HeroNode_new next=null;
			while(temp1!=null) {
					next=temp1.next;
					temp1.next=temp2.next;
					temp2.next=temp1;
					temp1=next;
			}
				headNode.next=temp2.next;   //这一步很重要
			}
	
		//逆向打印链表
		public void reverse_print(HeroNode_new headNode) {
			//利用栈先进后出的特点,将节点装入栈中,然后从栈中弹出。
			Stack<HeroNode_new> stack=new Stack<HeroNode_new>();
			HeroNode_new temp=headNode.next;
			if(headNode.next==null) {
				System.out.println("链表为空");
				return;
			}
			while(temp!=null) {
					stack.push(temp);
					temp=temp.next;
				}
			while(stack.size()>0) {
				System.out.println(stack.pop());
			}	
		}
	}
	

//节点
class HeroNode_new{
//数据域、指针域
	public int id;
	public String name;
	public String nickname;
	public HeroNode_new next;
	
	public HeroNode_new(int id,String name,String nickname) {
		this.id=id;
		this.name=name;
		this.nickname=nickname;
	}
	
	public String toString()
	{	
		return "id="+id+" name="+name+" nickname="+nickname;
	}
}

一、时间频度:算法的基本操作语句的重复执行次数T(n)
时间复杂度:T(n)的同数量级函数,记作T(n)=o(f(n))。
一. 算法的时间复杂度:
1.常数阶O(1),消耗的时间不随某个变量的增长而增长。
2. 对数阶O(log2(n))
在这里插入图片描述
3. 线性阶O(n)
在这里插入图片描述
for循环里面的代码会执行n遍,故消耗的时间随着n的变化而变化。

  1. 线性对数阶O(nlogN)
    在这里插入图片描述
    将时间复杂度为O(logn)的代码循环N遍,则时间复杂度就是n*O(logN),也就是O(nlogN).

  2. 平方阶O(n的平方)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 平均时间复杂度和最坏时间复杂度 在这里插入图片描述

排序算法之堆排序

一. 堆

  1. 完全二叉树

  2. 每个结点的值都大于或等于其左右孩子结点的值—大顶堆
    在这里插入图片描述
    特点:

     arr[i]>=arr[2*i+1]&&arr[i]>=arr[2*i+2]
    
      升序采用大顶堆
    
  3. 每个结点的值都小于或等于其左右孩子结点的值—小顶堆
    特点:

     arr[i]<=arr[2*i+1]&&arr[i]<=arr[2*i+2]
    
      降序采用小顶堆
    

二、堆排序的基本思想:
在这里插入图片描述

二叉排序树(Binary Sort Tree):

  1. BST:对于二叉排序树的任何一个非叶子结点,左子节点的值比当前节点的值小,右子节点比当前节点的值大。
  2. 示例:
    在这里插入图片描述

一. 数组、链式、树存储方式的分析

  1. 数组:
    a. 优点:可以通过下标直接访问元素,对于有序数组可以使用二分查找提高检索速度。
    b. 缺点:若要插入元素,则会整体移动,效率低
    总结:访问快,插入慢

  2. 链式
    a. 优点:插入删除比较方便
    b. 缺点:检索慢,需要从头节点开始逐个遍历。

  3. 树:
    a. 优势:可提高数据存储和读取的效率。
    eg:二叉排序树,可保证数据的检索速度和插入删除修改速度

二、二叉树

  1. 二叉树示意图:
    在这里插入图片描述
  2. 满二叉树示意图:
    在这里插入图片描述
  3. 完全二叉树示意图:
    在这里插入图片描述

三、二叉树的遍历(前序、中序、后序)
在这里插入图片描述

  1. 思路:
    类:
    节点类(id,name,leftnode,rightnode)
class HeroNode{
	private int id;
	private String name;
	private HeroNode leftHeroNode;
	private HeroNode rightHeroNode;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public HeroNode getLeftHeroNode() {
		return leftHeroNode;
	}
	public void setLeftHeroNode(HeroNode leftHeroNode) {
		this.leftHeroNode = leftHeroNode;
	}
	public HeroNode getRightHeroNode() {
		return rightHeroNode;
	}
	public void setRightHeroNode(HeroNode rightHeroNode) {
		this.rightHeroNode = rightHeroNode;
	}
	
	public HeroNode(int id,String name) {
		this.id=id;
		this.name=name;
		
	}

new节点

//构建节点
		HeroNode root=new HeroNode(1, "qqq");
		HeroNode node2=new HeroNode(2, "www");
		HeroNode node3=new HeroNode(3, "eee");
		HeroNode node4=new HeroNode(4, "rrr");
		HeroNode node5=new HeroNode(5, "ttt");

二叉树类(root)

class Binary_Tree{
	private HeroNode root;      //root节点
	public void setRoot(HeroNode root) {
		this.root=root;
	}
}

new 树:

//构建树
		BinaryTree binaryTree=new BinaryTree();
		binaryTree.setRoot(root);
		root.setLeftHeroNode(node2);
		root.setRightHeroNode(node3);
		node2.setRightHeroNode(node4);
		node3.setLeftHeroNode(node5);

方法:
前序遍历

//在类HeroNode中
public void preOrder() {
		//this节点开始进行前序遍历,根、左、右---注意判断(终止)条件
		System.out.println(this.toString());
		if(this.leftHeroNode!=null) {
			this.leftHeroNode.preOrder();
		}
		if(this.rightHeroNode!=null) {
			this.rightHeroNode.preOrder();
		}
	}

中序遍历

//中序遍历    要先找到最左子节点
	public void midOrder() {
	  //左、根、右
		if(this.leftHeroNode!=null) {
			this.leftHeroNode.preOrder();
		}
		System.out.println(this.toString());
		if(this.rightHeroNode!=null) {
			this.rightHeroNode.preOrder();
		}
	}

后序遍历

//后序遍历
	public void lastOrder() {
		//左、右、根
		if(this.leftHeroNode!=null) {
			this.leftHeroNode.preOrder();
		}
		if(this.rightHeroNode!=null) {
			this.rightHeroNode.preOrder();	
		}
		System.out.println(this.toString());
	}

四、二叉树的查找

  1. 思路:按照前序、中序、后序的方法挨个查找
    在这里插入图片描述
    根据传入的id号进行查找
    在这里插入图片描述

  2. 三种查找方式之前序查找

//前序查找
	public HeroNode1 preSearch(int id) {
		HeroNode1 resultHeroNode=null;
		System.out.println("进入前序查找...");
		if(this.id==id) {
			return this;
		}
		if(this.leftHeroNode!=null) {
			resultHeroNode=this.leftHeroNode.preSearch(id);
		}
		if(resultHeroNode!=null) {
			return resultHeroNode;
		}
		if(this.rightHeroNode!=null) {
			resultHeroNode=this.rightHeroNode.preSearch(id);
		}
		return resultHeroNode;
	}
  1. 三种查找方式之中序查找
//中序查找
	public HeroNode1 midSearch(int id) {
		HeroNode1 resultHeroNode=null;
		if(this.leftHeroNode!=null) {
			resultHeroNode=this.leftHeroNode.midSearch(id);
		}
		if(resultHeroNode!=null) {
			return resultHeroNode;
		}
		System.out.println("进入中序查找...");
		if(this.id==id) {
			return this;
		}
		if(this.rightHeroNode!=null) {
			resultHeroNode=this.rightHeroNode.midSearch(id);
		}
		return resultHeroNode;
	}
  1. 三种查找方式之后序查找
public HeroNode1 lastSearch(int id) {
		HeroNode1 resultHeroNode=null;
		if(this.leftHeroNode!=null) {
			resultHeroNode=this.leftHeroNode.lastSearch(id);
		}
		if(resultHeroNode!=null) {
			return resultHeroNode;
		}
		if(this.rightHeroNode!=null) {
			resultHeroNode=this.rightHeroNode.lastSearch(id);
		}
		if(resultHeroNode!=null) {
			return resultHeroNode;
		}
		System.out.println("进入后序查找...");
		if(this.id==id) {
			return this;
		}
		return resultHeroNode;
	}

五、二叉树之删除节点

自平衡二叉(搜索)树之AVL树

平衡二叉树的实现方式:AVL、红黑树、替罪羊树、Treap、伸展树

  1. 引入原因:对二叉排序树的一个改进。
    在这里插入图片描述
  2. 平衡二叉树的特点:
    a. 本质上是一颗二叉排序树
    b. 一颗空树,或者它的左右两个子树的高度差的高度差的绝对值不超过1。
    c. 左右两个子树都是一颗平衡二叉树。
    d. 每个节点的平衡因子只可能是1、0、-1
    e. 搜索、添加、删除的时间复杂度是O(logn)
    在这里插入图片描述
  3. 基本概念:
    平衡因子:某节点左子树高度-右子树高度
    在这里插入图片描述eg: 7----> 左子树高度:2 右子树高度:4 平衡因子:-2

添加节点不会导致父节点和非祖先节点的失衡,只会导致其祖先节点的失衡。
在这里插入图片描述

  1. 构建平衡二叉树方法:
    步骤:
    (1)找平衡因子=2的节点
    (2)找插入新节点后失去平衡的最小子树
    (3)平衡调整
    2要素:
    (1)距离插入节点最近
    (2)平衡因子绝对值>1的结点作为根节点
    典型例子1:
    在这里插入图片描述
    典型例子2:
    在这里插入图片描述

a. 左旋转
条件:rightHeight-leftHeight>1 (右高)
步骤:首先要计算左右子树的高度
示意图:RR(失衡节点和新添加节点之间的大概安息)
在这里插入图片描述
在这里插入图片描述
b. 右旋转
条件:leftHeight-rightHeight>1 (左高)
步骤:首先要计算左右子树的高度
示意图:LL
在这里插入图片描述
右旋转进行调整:
在这里插入图片描述
注::该棵子树调整之后和原来的子树高度一致,所以不会导致以上树的失衡。

c. LR--RR左旋转,LL右旋转(双旋)
示意图:LR

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
d. RL
示意图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自平衡二叉(搜索)树之红黑树

  1. 底层数据结构:特殊的二叉查找树

  2. 产生原因: 避免下述现象产生。在这里插入图片描述

  3. 红黑树的特点:
    a. 每个节点不是黑色就是红色
    b. 不会有连在一起的红色节点
    c. 根节点都是黑色
    d. 每个红色节点的两个子节点都是黑色。叶子节点都是黑色;可以达到相对平衡的状态

  4. eg:示意图:
    在这里插入图片描述

  5. 变换规则
    在这里插入图片描述

  6. 变换示例:
    在这里插入图片描述
    变色::
    在这里插入图片描述
    旋转(左旋):
    在这里插入图片描述
    右旋:
    在这里插入图片描述

多叉树

  1. 引入原因:
    在这里插入图片描述
    在这里插入图片描述
    最坏情况下,磁盘的读写次数等于树的高度。
    在这里插入图片描述
    比较次数虽然相同,但是都是在内存中进行的,这次对于磁盘的读写只用了3次。提高了性能。

面试趴:

一、为什么要用B树:

  1. 一次磁盘I/O操作会把一个节点的所有内容加载到内存中(这个占据大部分时间),然后在内存中进行查找比较(这个时间几乎可以忽略)。这时,当一个节点的数据量较大时,相应的磁盘I/O操作次数会减少。

  2. 特点:
    允许每个节点可以有更多的数据项和子节点

  3. eg: 2-3树
    在这里插入图片描述

B树结构:

  1. 特点:
    a. 重新组织节点,降低树的高度,减少i/o读写次数提升效率。
    b. 将树的度M设置为1024,在600亿个元素中最多只需要4次I/O操作就可读取到想要的元素。
    c. 每个索引对应这自己的数据,没有任何冗余。
    自己理解:每一个节点存放的数据多,I/O次数降低

  2. 应用:B树、B+树广泛应用于文件存储系统以及数据库系统中。

  3. 示意图:
    在这里插入图片描述
    在这里插入图片描述

B树之2-3树:

  1. 特点:由二节点(没有子节点/有两个子节点)和三节点(没有字节点/有三个子节点)构成的树。
  2. 图示
    在这里插入图片描述
    构建规则:
    在这里插入图片描述

B+树:

  1. 特点:
  2. 示意图:在这里插入图片描述

在这里插入图片描述
详情见mysql面试

B*树

在这里插入图片描述

查找算法:

在这里插入图片描述

面试题之归并排序思路

 * 归并排序

 * 是一种稳定排序
 * 是采用分治法的一个典型应用
 * 最佳情况:T(n) = O(n) 最差情况:T(n) = O(nlogn) 平均情况:T(n) = O(nlogn)
 
 * 算法描述:先分后治

 * 分:将一整块分成两份   左边:left,mid   右边:mid+1,right
 * 合并:(排序+合并) 用一个新的数组,对两边进行排序。


package com.weichen.paixu;
import java.util.Arrays;
==
public class Merge_sort {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr= {3,2,5,1,6,5,4,2};
		int[] temp=new int[8];
		mergesort(arr, 0, arr.length-1, temp);
		System.out.println(Arrays.toString(arr));
	}
	//采用
	public static void mergesort(int[] arr,int left,int right,int[] temp) {
		if(left<right) {
			int mid=(left+right)/2;
			//左边
			mergesort(arr, left, mid, temp);
			//右边
			mergesort(arr, mid+1, right, temp);
			merge(arr, left, mid, right, temp);
		}
	}
	public static void merge(int[] arr,int left,int mid,int right,int[] temp) {
			int i=left;
			int j=mid+1;
			int t=0;			
			while(i<=mid && j<=right) {
				if(arr[i]<=arr[j]) {
					temp[t++]=arr[i++];	
				}
				if(arr[j]<arr[i]) {
					temp[t++]=arr[j++];
				}
			}
			while(i<=mid) {
				temp[t++]=arr[i++];
			}
			while(j<=right) {
				temp[t++]=arr[j++];
			}			
			//把temp中的值相应的付给arr			
			int h=0;
			int tempLeft=left;
			while(tempLeft<=right) {
				arr[tempLeft++]=temp[h++];
			}
		}
}

排序算法复杂度:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值