基础算法-排序算法和查找算法

十大排序算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wSD6HGEH-1659950611461)(Java学习之算法/image-20220705162319850.png)]

冒泡排序

比较相邻元素,如果前一个比后一个大,则交换他们

冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但是这种改进对于提升性能没有太大作用

选择排序

在未排序的序列中找到最小的元素,存放到起始位置。再找其余最小的排到已排序列的末尾,直到所有元素都排序完毕;

唯一好处就是不占额外空间了吧

插入排序

把第一个排列元素看作有序序列,第二个往后是未排列序列;

从头到尾扫描未排列序列,将扫描到的元素插入到有序序列的适当位置(相同时,插入到相等元素的后边)

有一种优化算法–折半插入

希尔排序

希尔排序又称递减增量排序算法,是非稳定排序算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ongu7OkT-1659950611463)(Java学习之算法/image-20220705174535956.png)]

public static int[] shell_demo2(int[] list){//希尔排序
    int skip=list.length/2;
    do {
        for (int i = skip; i < list.length; i++) {
            //System.out.printf("每%d轮的i:%d值\n",skip,i);
            int j = i - skip;int temp=list[i];
               for (;j >=0&&temp<list[j]; j -= skip) {
                list[i] = list[j];
            }
            list[j+skip] = temp;
        }
        skip /= 2;
    } while (skip > 0);
    return list;
}

归并排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dIVvjcmi-1659950611464)(Java学习之算法/image-20220705174838503.png)]

public static int[] sort(int[] a,int low,int high){
    int mid = (low+high)/2;
    if(low<high){
        sort(a,low,mid);
        sort(a,mid+1,high);
        //左右归并
        merge(a,low,mid,high);
    }
    return a;
}

public static void merge(int[] a, int low, int mid, int high) {
    int[] temp = new int[high-low+1];
    int i= low;
    int j = mid+1;
    int k=0;
    // 把较小的数先移到新数组中
    while(i<=mid && j<=high){
        if(a[i]<a[j]){
            temp[k++] = a[i++];
        }else{
            temp[k++] = a[j++];
        }
    }
    // 把左边剩余的数移入数组
    while(i<=mid){
        temp[k++] = a[i++];
    }
    // 把右边边剩余的数移入数组
    while(j<=high){
        temp[k++] = a[j++];
    }
    // 把新数组中的数覆盖nums数组
    for(int x=0;x<temp.length;x++){
        a[x+low] = temp[x];
    }
}

快速排序

从数列中挑出一个元素,作为基准;重新排列数列,所有比基准小的摆放在基准前面,所有比基准大的元素摆到基准后边(相同的数字可以放在任意一边);递归的把小于基准值元素的子序列和大于基准元素的字序列进行排序

public static int[] quick_sort(int arr[], int left, int right) {//quick_sort(list, 0, list.length - 1)
    if (left < right) {
        int partitionindex = partition(arr, left, right);
        quick_sort(arr, left, partitionindex - 1);
        quick_sort(arr, partitionindex + 1, right);
    }
    return arr;
}

public static int partition(int[] arr, int left, int right) {
    int index = left + 1;
    for (int i = index; i <= right; i++) {
        if (arr[i] < arr[left]) {
            swap(arr, i, index);
            index++;
        }
    }
    swap(arr, left, index - 1);
    return index - 1;
}

public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

随机化快速排序

就是在设置基准值的时候不采用最左边的那个数作为基准值,而是采用一个随机数列取一个随机的索引跟最左边的进行交换,然后进行排序

双路快速排序

就是两边都设置索引值,把比基准值小的i+1并进行交换,比基准值大的j-1,然后进行i和j上的值进行交换,之后i+1,j-1;然后进行下一轮,直到i>j时候暂停,这时候就说明全部都已经比较过一次了;

三路排序算法

是二路快速排序的进一步改进版本,将值分为三部分,小于V,等于V,大于V,V是标定值;三路快速排序,对处理大量重复元素的数组非常有效,提高了快速排序的过程

public static void sort(int[] arr,int l,int r){
    if(l>r){
        return;
    }
    int refer=arr[l];
    int lt=l;
    int gt=r+1;
    int i=l+1;
    while(i<gt){
        if(arr[i]<refer){
            swap(arr,i,lt+1);
            i++;
            lt++;
        }
        else if(arr[i]>refer){
            swap(arr,i,gt-1);
            gt--;
        }
        else{
            i++;
        }
    }
    //交换标准值
    System.out.println(i+"||"+lt+"||"+gt);
    swap(arr,l,lt);
    sort(arr,0,lt-1);
    sort(arr,gt,r);
}
public static void swap(int[] arr ,int i,int j){
    int temp=arr[i];
    arr[i]=arr[j];
    arr[j]=temp;
}

堆排序

先构造根据要求序列构造相应的大堆或者小堆,然后把堆顶元素和最后一个元素进行互换,得到最大值或者最小值,然后对其他在进行大堆或者小堆排序,重复操作,直到把所有元素都排序完成

public static int[]  duisort(int[] sourceArray){
    int[] arr= Arrays.copyOf(sourceArray,sourceArray.length);
    buildMaxheap(arr);
    int len=arr.length;
    for (int i=len-1;i>0;i--){
        swap(arr,0,i);
        len--;
        handif(arr,0,len);
    }
    return arr;
}
public static void buildMaxheap(int[] arr){
    for (int i=(arr.length/2);i>=0;i--){
        handif(arr,i,arr.length);
    }
}
public static void handif(int[] arr,int i,int len){
    int left=2*i+1;
    int right=2*(i+1);
    int largest=i;
    if(left<len&&arr[left]>arr[largest]){
        largest=left;
    }
    if(right<len&&arr[right]>arr[largest]){
        largest=right;
    }
    if(largest!=i){
        swap(arr,i,largest);
        handif(arr,largest,len);//对分支内的所有序列进行排序
    }
}
public static void swap(int[] arr,int i ,int j){
    int temp=arr[i];
    arr[i]=arr[j];
    arr[j]=temp;
}

计数排序

创建一个数列,数列的长度就是所给数组中的最大值,遍历所给数组arr,新数列中对应的序号的内容加一,遍历新创数组,可以得到对应的数列

public static int[] jishu(int[] arr) {
    int max = getMaxValue(arr);

    int[] result = new int[arr.length];
    int[] arr_num = new int[max];
    Arrays.fill(arr_num, 0);
    for (int item : arr) {
        arr_num[item - 1] += 1;
        //System.out.println(Arrays.toString(arr_num));
    }
    int hassortindex = 0;
    for (int i = 0; i < max; i++) {
        if (arr_num[i] > 0) {
            for (int j = arr_num[i]; j > 0; j--) {
                result[hassortindex] = i + 1;
                hassortindex++;
            }
        }
    }
    return result;
}

public static int getMaxValue(int[] arr) {
    int Max = 0;
    for (int item : arr) {
        if (item > Max) {
            Max = item;
        }
    }
    return Max;
}

桶排序

什么时候最快:输入的数据可以均匀分配到每一个桶中;

什么时候最慢:当输入的数据被分配到同一个桶中;

利用多维数组,每一行作为一个桶的概念,每一个桶的大小是可以自己定的

public static int[] tong_sort(int[] list,int bucketSize){//tong_sort(list,5)
    int[] arr= Arrays.copyOf(list,list.length);
    int maxValue=arr[0];
    int minValue=arr[0];
    for(int item :arr){
        if(item>maxValue)maxValue=item;
        else if(item<minValue)minValue=item;
    }
    int bucketCount=(int)Math.ceil((double)(maxValue-minValue)/bucketSize);
    System.out.println("桶的数量是"+bucketCount);
    int[][] buckets=new int[bucketCount][0];
    for (int item :arr){
        int index=(item-minValue)/bucketSize;
        buckets[index]=arrAppend(buckets[index],item);
    }
    int arrindex=0;
    for (int[] bucket:buckets){
        if(bucket.length==0){
            continue;
        }
        demo_quick.quick_sort(bucket, 0, bucket.length - 1);//使用快排对每一个桶内元素进行排序
        for (int value:bucket){
            arr[arrindex]=value;
            arrindex++;
        }
    }
    return arr;
}
public static int[] arrAppend(int[] arr ,int value){
    arr=Arrays.copyOf(arr,arr.length+1);
    arr[arr.length-1]=value;
    return arr;
}

基数排序

基数排序:根据键值的每一位数字来分配桶;

计数排序:每个桶只存储单一数值;

桶排序:每一个桶存储一定范围的数值;

public static void main(String[] args) {
    int[] list= new int[]{42, 20, 17, 13, 28, 14, 23, 15};
    System.out.println(Arrays.toString(radixSort(list)));//假定全部都是正整数的二位数,主要是为了了解算法实现
}
public static int[] radixSort(int [] arr){
    int[][] arr_num=new int[10][0];
    for (int j : arr) {
        arr_num[j % 10] = arrAdd(arr_num[j % 10], j);
    }
    int index=0;
    for(int i=0;i<10;i++){
        for(int item :arr_num[i]){
            if(item!=0){
                arr[index]=item;
                index++;
            }
        }
    }
    /System.out.println("中间的数列:"+Arrays.toString(arr));
    for (int i=0;i<10;i++)
        Arrays.fill(arr_num[i],0);
    for (int i=0;i<arr.length;i++){
        arr_num[arr[i]/10]=arrAdd(arr_num[arr[i]/10],arr[i]);
    }
    index=0;
    for(int i=0;i<10;i++){
        for(int item :arr_num[i]){
            if(item!=0){
                arr[index]=item;
                index++;
            }
        }
    }
    return arr;
}
public static int getBucketCount(int min,int max){
    int count=0;
    return count;
}
public static void swap(int[] arr,int i,int j){
    int temp=arr[i];
    arr[i]=arr[j];
    arr[j]=temp;
}
public static int[] arrAdd(int[] list,int value){
    int[] arr=Arrays.copyOf(list, list.length+1);
    arr[list.length]=value;
    return arr;
}

排序算法的衍生问题

归并排序和快速排序,都使用了分治算法;分治算法就是将原来的问题分割成同等结构的问题,之后将子问题逐个解决,原问题也就得到了解决

查找算法

顺序查找

折半查找(二分查找)

说明:元素必须是有序的,如果是无序的则要先进行排序操作

复杂度分析 :最坏情况下log2(n+1);期望时间复杂度O(log2n)

public int BinarySearch(T[] nums,T t){
    int left=0;int right=nums.length;int ans=0;
    while(left<=right){
        int mid=(left+right)>>1;
        if(t.compareTo(nums[mid])<=0){
            ans=mid;
            right=mid-1;
        }else{
            left=mid+1;
        }
    }
    return ans;
}

插值查找

复杂度分析:查找成功或者失败的时间复杂度均为O(log2(log2n))。

public  int interpolationSearch(int[] nums,int key){
    int low=0;int height=nums.length-1;int ans=-1;
    while(low<height){
        int mid=low+(int)((float)(key-nums[low])/(nums[height]-nums[low])*(height-low));
        if(nums[mid]==key)return mid;
        if(nums[mid]>key)height=mid-1;
        if(nums[mid]<key)low=mid+1;
    }
    return ans;
}

斐波那契查找

是二分查找的一种提升算法,通过运用黄金比例的概念再数列中进行选点进行查找,提高查找效率。同样的斐波那契查找也属于一种有序的查找算法。要求表中记录的个数为某个斐波那契数-1 即length=F(k)-1

public int fibonacciSearch(T[] nums,T t){
    int ans=-1,left=0,right=nums.length-1;
    List<Integer> indexes=buildFibonacci(nums.length);
    int k=indexes.size()-1;
    if(indexes.size()==0){
        System.out.println("您输入的字符串有误");
        return -2;
    }
    while(left<=right){
        int mid=left+indexes.get(k)-1;
        if(nums[mid].compareTo(t)<0){left=mid+1;k-=2;}
        else if(nums[mid].compareTo(t)>0){right=mid-1;k-=1;}
        else{ ans=mid;break;}
    }
    return ans;
}
public List<Integer> buildFibonacci(int num){
    List<Integer> fibo=new ArrayList<>();
    fibo.add(1);
    fibo.add(1);
    Integer add_num=0;
    for(int i=2;add_num<num;i++){
        add_num=fibo.get(i-1)+fibo.get(i-2);
        fibo.add(add_num);
    }
    if(add_num!=num)return new ArrayList<>();
    return fibo;
}

树表查询

二叉排序树的建立以及二叉排序树的搜索

private class Node{
    private Key key;
    private Node left,right;
    public Node(Key key){
        this.key=key;
        left=right=null;
    }
}
private Node root;//根节点
private int count;//树中的节点个数
public Demo_Deep(){
    root=null;
    count=0;
}
public int size(){
    return count;
}
public boolean isEmpty(){
    return count==0;
}
public boolean search(Key key){
    return search(root,key);
}
private boolean search(Node node,Key key){
    boolean flag=false;
    if(node==null){
        return false;
    }
    if(node.key.compareTo(key)==0){
        System.out.println(node.key);
        return true;
    }else if (node.key.compareTo(key)>0){
        return search(node.left,key);
    }else
        return search(node.right,key);
}

public void insert(Key key){
    root=insert(root,key);
}
private Node insert(Node node, Key key){
    if(node==null){
        count++;
        return new Node(key);
    }
    if (key.compareTo(node.key)<0){
        node.left=insert(node.left,key);
    }else{
        node.right=insert(node.right,key);
    }
    return node;
}

分块查询

哈希表查找

链表

单向链表

单向链表的创建

public class DemoLinkedList<T> implements Iterable<T>{
    private Node head;
    private int N;
    private class Node{
        T item;
        Node next;
        public Node(T item,Node next){
            this.item=item;
            this.next=next;
        }
    }
    public DemoLinkedList(){
        this.head=new Node(null,null);
        this.N=0;
    }

    //清空链表
    public void clear(){//设置头结点的下一个结点为空,把链表的长度归为0
        head.next=null;
        this.N=0;
    }


    //返回链表的长度
    public int length(){
        return N;
    }

    //是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //获取指定位置i处的元素
    public T get(int i){
        Node node=head.next;
        for(int j=0;j<i;j++){
            node=node.next;
        }
        return node.item;
    }
    //添加指定的元素
    public void  insert(T t) {
        Node n=head;
        while(n.next!=null){
            n=n.next;
        }
        //创建新节点,让最后一个结点指向新结点
        n.next=new Node(t,null);
        //链表长度加一
        N++;
    }
    //添加指定的一个元素,再线性表的第i个元素之前插入一个值为t的数据元素
    public void insert(int i,T t){
        Node pre=head;
        for(int index=0;index<=i-1;index++){
            pre=pre.next;
        }
        Node curr=pre.next;
        Node newnode=new Node(t,curr);
        pre.next=newnode;
        N++;
    }
    //删除并返回线性表中的第i个数据元素
    public T remove(int i){
        Node pre=head;
        for(int index=0;index<=i-1;index++){
            pre=pre.next;
        }
        Node node=pre.next;
        pre.next=node.next;
        N--;
        return node.item;
    }
    //返回线性表中首次出现的指定的数据元素的位序号,若不存在则返回-1
    public int indexOf(T t){
        Node node=head;
        int index=0;
        while(node.next!=null){
            if(node.item==t){
                return index;
            }
            index++;
            node=node.next;
        }
        return -1;
    }

    @Override
    public Iterator<T> iterator() {
        return new LIterator();
    }
    //提供一个遍历方法
    private class LIterator implements Iterator{
        private Node n;
        public LIterator(){
            this.n=head;
        }
        @Override
        public boolean hasNext() {
            return n.next!=null;
        }

        @Override
        public Object next() {
            n=n.next;
            return n.item;
        }
    }
} 

双向链表

双向链表的实现

public class TowWayLinkList<T> implements Iterable<T>{
    private Node head;
    private Node last;
    private int N;
    public class Node{
        T data;
        Node pre;
        Node next;
        public  Node(T data,Node pre,Node next){
            this.data=data;
            this.pre=pre;
            this.next=next;

        }
    }
    public TowWayLinkList(){
        //初始化头结点和尾结点
        this.head=new Node(null,null,null);
        this.last=null;
        //初始化链表长度
        this.N=0;
    }
    public void clear(){
        this.head.next=null;
        this.head.pre=null;
        this.head.data=null;
        this.last=null;
        this.N=0;
    }
    public int length(){
        return N;
    }
    public boolean isEmpty(){
        return N==0;
    }
    public  T getFirst(){
        if(isEmpty())return null;
        return head.next.data;
    }
    public T getLast(){
        if(isEmpty()) return null;
        return last.data;
    }
    public void insert(T t){
        if(isEmpty()){//链表为空
            Node newNode  =new Node(t,head,null);
            head.next=newNode;
            last=newNode;
            last.next=last;
        }else{//链表不为空
            Node oldlast=last;
            Node newNode=new Node(t,oldlast,null);
            oldlast.next=newNode;
            last=newNode;
        }
        N++;
    }
    public void insert(int i,T t){
        Node n=head;
        for(int index=0;index<i&&index<N;index++){
            n=n.next;
        }
        Node nextNode=n.next;
        //创建新节点
        Node newNode=new Node(t,n,nextNode);
        n.next=newNode;
        nextNode.pre=newNode;
        N++;
    }
    public T get(int i){
        Node n=head.next;
        for(int index=0;index<i&&index<N;index++){
            n=n.next;
        }
        return n.data;
    }
    public int indexOf(T t){
        Node n=head;
        for(int index=0;n.next!=null;index++){
            n=n.next;
            if(n.next.equals(t)){
                return index;
            }
        }
        return -1;
    }
    public T remove(int i){
        Node p=head;
        for(int index=0;index<i;index++){
            p=p.next;
        }
        Node node=p.next;
        Node nextNode=node.next;
        p.next=nextNode;
        nextNode.pre=p;
        N--;
        return node.data;
    }

    @Override
    public Iterator<T> iterator() {//提供一个遍历方法
        return new TIterator();
    }
    private class TIterator implements Iterator{
        private Node n;
        public TIterator(){
            this.n=head;
        }
        @Override
        public boolean hasNext() {
            return n.next!=null;
        }

        @Override
        public Object next() {
            n=n.next;
            return n.data;
        }
    }
}

单链表反转

//用来反转整个链表
public void reverse(){
    //判断是否为空链表,若为空
    if(isEmpty()){
        return;
    }else{
        reverse(head.next);
    }
}
//反转指定的curr,并且把反转之后的结点
public Node reverse(Node curr){
    if(curr.next==null){
        head.next=curr;
        return curr;
    }
    //递归的调用当前结点的下一个结点,返回值就是利埃纳表反转后,当前结点的下一个结点
    Node pre=reverse(curr.next);
    //让返回的结点的下一个结点
    pre.next=curr;
    curr.next=null;
    System.out.println(curr.item+"排序完成");
    return curr;
}

快慢指针

中间值问题、单链表是否有环路、环路的入口问题

public T getMid(){
    Node fast=head.next;
    Node slow=head.next;
    while(fast!=null&&fast.next.next!=null){
        fast=fast.next.next;
        slow=slow.next;
    }
    return slow.item;
}
public boolean isCircle(){
    Node fast=head.next;
    Node slow=head.next;
    while(fast.next.next!=null&&fast!=null){
        fast=fast.next.next;
        slow=slow.next;
        if(fast.equals(slow)){
            return true;
        }
    }
    return false;
}
public T CircleInter(){
    Node fast=head.next;
    Node slow=head.next;
    Node temp=null;
    while(fast!=null&&fast.next.next==null){
        fast=fast.next.next;
        slow=slow.next;
        if(fast.equals(slow)){
            temp= head.next;
            continue;
        }
        if(temp!=null){
            temp=temp.next;
            if(temp.equals(slow)){
                break;
            }
        }
    }
    return temp.item;
}

约瑟夫问题

约瑟夫问题可以转化为一个环形链表来处理这个问题,自杀的人通过删除结点来模拟

栈是一个基于先进先出的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表

栈的实现

案例

括号匹配问题

public static boolean isMatch(String str){
    int num=0;
    Demo_stack<Character> charsStack=new Demo_stack<Character>();
    int index=0;
    while(index<str.length()){
        char ch=str.charAt(index);
        charsStack.push(ch);
        index++;
    }
    for(Character ch:charsStack){
        System.out.println(ch);
    }
    for(int i=0;i<str.length();i++){
        Character ch=charsStack.pop();
        if(ch.equals(')'))num++;
        if(ch.equals('('))num--;
        if(num<0){
            return false;
        }
    }
    if(num>0)return false;
    if(num==0)return true;
    return false;
}

逆波兰表达式

就是后缀表达式的实现,一般人们都习惯于使用,中缀表达式。即a+b,逆波兰表达式用ab+表示

队列

队列的创建这次也是使用的线性表

public void enqueue(T t){//向队列中加入元素
    if(last==null){
        last=new Node(t,null);
        head.next=last;N++;
    }
    else{//当前last结点不为空
        Node oldlast=last;
        last=new Node(t,null);
        oldlast.next=last;N++;
    }

}
public T dequeue(){//从队列中取出元素

    if(isEmpty())return null;
    Node oldfirst=head.next;
    head.next=oldfirst.next;
    N--;
    if(isEmpty()){
        last=null;
    }
    return oldfirst.item;
}

是一类特殊的数据结构的统称;堆可以看作一颗完全二叉树的数组对象;堆中的某个节点的之总是不大于或不小于其父节点的值;

大根堆(小根堆)的建立,大根堆(小根堆)的取出其中的元素

public class Demo_shiftDown <T extends Comparable>{
    protected T[] data;
    protected int count;
    protected int capacity;
    public Demo_shiftDown(int capacity){
        data=(T[])new Comparable[capacity+1];
        count=0;
        this.capacity=capacity;
    }
    public int size(){
        return count;
    }
    public boolean isEmpty(){
        return count==0;
    }
    public void insert(T item){
        assert count+1<capacity;
        data[count+1]=item;
        count++;
        shiftUp(count);
    }
    private void shiftUp(int k){
        while(k>1&&data[k].compareTo(data[k/2])>0){
            System.out.println(k+"子节点是"+data[k]+"父节点是"+data[k/2]+"||是否要换位置"+(data[k].compareTo(data[k/2])>0));
            swap(k,k/2);
            k/=2;
        }
    }
    public void swap(int i,int j){
        T temp=data[i];
        data[i]=data[j];
        data[j]=temp;
    }
    //这往上是大根堆(小根堆)的建立过程

    //从大堆中取出堆顶元素,即去除所存储的最大数据
    public T extractMax(){
        assert count>0;
        T ret=data[1];
        swap(1,count);
        count--;
        shiftDown(1);
        return ret;
    }
    //获取最大堆中的堆顶元素
    public T getMax(){
        assert (count>0);
        return data[1];
    }
    public void shiftDown(int k){
        while(2*k<count){
            int j=2*k;
            if(j+1<count&&data[j+1].compareTo(data[j])>0)j++;
            if(data[k].compareTo(data[j])>=0)break;
            swap(k,j);
            k=j;
        }
        System.out.println("shiftDown结束");
    }

    public static void main(String[] args) {
        Demo_shiftDown<Integer> heapShiftDown = new Demo_shiftDown<>(100);
        // 堆中元素个数
        int N = 100;
        // 堆中元素取值范围[0, M)
        int M = 100;
        for( int i = 0 ; i < N ; i ++ )
            heapShiftDown.insert( new Integer((int)(Math.random() * M)) );
        Integer[] arr = new Integer[N];
        // 将最大堆中的数据逐渐使用extractMax取出来
        // 取出来的顺序应该是按照从大到小的顺序取出来的
        for( int i = 0 ; i < N ; i ++ ){
            arr[i] = heapShiftDown.extractMax();
            System.out.print(arr[i] + " ");
        }
        // 确保arr数组是从大到小排列的
        for( int i = 1 ; i < N ; i ++ )
            assert arr[i-1] >= arr[i];
    }
}

基础堆排序

①基础堆排序,建立大根堆或者小根堆之后②最大元素跟最后一个元素换位置之后count-1,③然后从最后一个非叶子节点向上比较向左依次比较④重新建立大根堆和小根堆;重复上述②③④动作,直到堆中的元素全部取出

优化堆排序

主要是将上述的第三步,改成了比较少数,跟改变值有分支关系的就行了,例如:62和17交换位置之后,只需要给更改节点到最下边叶子节点的值就可以了,但是基础堆排序就需要遍历整个非叶子节点,重新建堆,减少了比较次数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgR1rpYl-1659950611466)(Java学习之算法/image-20220712130442131.png)]

索引堆及其优化

如果堆中存储的元素较大,那么进行交换就需要消耗大量的时间,这个时候就需要使用索引堆,堆中存储的是数组的索引,操作的也都是索引

protected T[] data;      // 最大索引堆中的数据
protected int[] indexes;    // 最大索引堆中的索引
protected int count;
protected int capacity;
// 构造函数, 构造一个空堆, 可容纳capacity个元素
public Demo_IndexHeap(int capacity){
    data = (T[])new Comparable[capacity+1];
    indexes = new int[capacity+1];
    count = 0;
    this.capacity = capacity;
}
// 返回索引堆中的元素个数
public int size(){
    return count;
}
// 返回一个布尔值, 表示索引堆中是否为空
// 向最大索引堆中插入一个新的元素, 新元素的索引为i, 元素为item
// 传入的i对用户而言,是从0索引的
public void insert(int i, T item){
    assert count + 1 <= capacity;
    assert i + 1 >= 1 && i + 1 <= capacity;
    i += 1;
    data[i] = item;
    indexes[count+1] = i;
    count ++;
    shiftUp(count);
}

// 从最大索引堆中取出堆顶元素, 即索引堆中所存储的最大数据
public T extractMax(){
    assert count > 0;
    T ret = data[indexes[1]];
    swapIndexes( 1 , count );
    count --;
    shiftDown(1);
    return ret;
}
// 从最大索引堆中取出堆顶元素的索引
public int extractMaxIndex(){
    assert count > 0;
    int maxIndex = indexes[1] - 1;
    swapIndexes( 1 , count );
    count --;
    shiftDown(1);
    return maxIndex;
}
// 获取最大索引堆中的堆顶元素
public T getMax(){
    assert count > 0;
    return data[indexes[1]];
}
public void print(){
    for(int i=1;i<count+1;i++){
        System.out.println("索引列表中索引序号为"+i+"索引中对应的索引"+getMaxIndex()+"对应的值是"+extractMax());
    }
}
// 获取最大索引堆中的堆顶元素的索引
public int getMaxIndex(){
    assert count > 0;
    return indexes[1]-1;
}
private void swapIndexes(int i, int j){
    int t = indexes[i];
    indexes[i] = indexes[j];
    indexes[j] = t;
}
//k是堆的索引
// 索引堆中, 数据之间的比较根据data的大小进行比较, 但实际操作的是索引
private void shiftUp(int k){
    while( k > 1 && data[indexes[k/2]].compareTo(data[indexes[k]]) < 0 ){
        swapIndexes(k, k/2);
        k /= 2;
    }
}

// 索引堆中, 数据之间的比较根据data的大小进行比较, 但实际操作的是索引
private void shiftDown(int k){
    while( 2*k <= count ){
        int j = 2*k;
        if( j+1 <= count && data[indexes[j+1]].compareTo(data[indexes[j]]) > 0 )
            j ++;
        if( data[indexes[k]].compareTo(data[indexes[j]]) >= 0 )
            break;
        swapIndexes(k, j);
        k = j;
    }
}

// 测试 IndexMaxHeap
public static void main(String[] args) {

    int N = 1000;
    Demo_IndexHeap<Integer> indexMaxHeap = new Demo_IndexHeap<>(N);
    for( int i = 0 ; i < N ; i ++ )
        indexMaxHeap.insert( i , (int)(Math.random()*N) );
    System.out.println(indexMaxHeap.getMax());
    System.out.println("最大值的索引"+indexMaxHeap.getMaxIndex());
    indexMaxHeap.print();
}

二分搜索树

若它的左子树不为空,左子树上所有的节点都小于他的根节点

若他的右子树不为空,右子树上所有的节点都小于它的根节点

public class Demo_insert<Key extends Comparable<Key>,Value> { 
	private class Node{
        private Key key;
        private Node left,right;
        public Node(Key key){
            this.key=key;
            left=right=null;
        }
    }
    private Node root;//根节点
    private int count;//树中的节点个数
    public Demo_insert(){
        root=null;
        count=0;
    }
    public int size(){
        return count;
    }
    public boolean isEmpty(){
        return count==0;
    }
}

二分搜索树的插入

public void insert(Key key){
    root=insert(root,key);
}
private Node insert(Node node,Key key){
    if(node==null){
        count++;
        return new Node(key);
    }
    if (key.compareTo(node.key)<=0){
        node.left=insert(node.left,key);
    }else{
        node.right=insert(node.left,key);
    }
    return node;
}

二分搜索树节点的插入

public boolean search(Key key){
    return search(root,key);
}
private boolean search(Node node,Key key){
    boolean flag=false;
    if(node==null){
        return false;
    }
    if(node.key.compareTo(key)==0){
        System.out.println(node.key);
        return true;
    }else if (node.key.compareTo(key)>0){
        return search(node.left,key);
    }else
        return search(node.right,key);
}

二分搜索树深度优先遍历

private void preOrder(Node node){//前序遍历,先遍历根节点,在遍历左节点,在遍历右节点
    if(node==null){
        return;
    }
    System.out.println(node.key+"  ");
    preOrder(node.left);
    preOrder(node.right);
}
private void inOrder(Node node){//中序遍历,先左节点,再根节点,再右节点
    if(node==null){
        return;
    }
    inOrder(node.left);
    System.out.println(node.key);
    inOrder(node.right);
}
private void postOrder(Node node){//后序遍历,先左子节点,再右节点,最后根节点
    if(node==null){
        return ;
    }
    postOrder(node.left);
    postOrder(node.right);
    System.out.println(node.key);
}

二分搜索树层级遍历

private void LevelOrder(){
    LinkedList<Node> q=new LinkedList<Node>();
    q.add(root);
    while(!q.isEmpty()){
        Node node=q.remove();
        System.out.println(node.key);
        if(node.left!=null){
            q.add(node.left);
        }
        if(node.right!=null){
            q.add(node.right);
        }
    }
}

二分搜索树节点删除

删除二分搜索树的最大或最小结点

①找到最大或者最小结点:遍历左子树或者右子树

②如果最小结点没有右子树,直接删除;如果最小结点有右子树,

private Node removeMin(Node node){
    if(node.left==null){
        Node rightNode=node.right;
        node.right=null;
        count--;
        return rightNode;
    }
    Node.left=removeMin(node.left);
    return node;
}

动态规划

笔记

笔记1:markDown上下标可以使用

<sub>下标</sub>
~下表~
<sup>上标</sup>
^上标^

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值