java-数据结构

一.链表

单向链表

java 复制代码
/单向链表
public class SinglyLinkedList implements Iterable<Integer> {
    //头节点
     private Node head =null;//头指针
    //节点类
    private static   class  Node{
        int value;//值
        Node next;//下一个节点的指针

        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }
    }
}

addFirst方法

java 复制代码
 public  void  addFirst(int value)
    {
        //1.链表为空
      //  head =new Node(value,null);
        //2.链表非空
        head =new Node(value,head

遍历链表

java 复制代码
    public  void  loop1(Consumer<Integer> consumer)
    {
        Node p =head;
        while (p!=null)
        {
           consumer.accept(p.value);
            p=p.next;
        }
    }
  

java 复制代码
  public  void  loop2(Consumer<Integer> consumer)
    {
        for (Node p =head;p!=null;p=p.next)
        {
            consumer.accept(p.value);
        }
    }

java 复制代码
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            SinglyLinkedList.Node p = head;

            @Override
            public boolean hasNext() {//是否有下一个元素
                return p != null;
            }

            @Override
            public Integer next() {//返回当前值,并指向下一个元素

                int v = p.value;
                p = p.next;
                return v;
            }


        };
    }

Test类e

java 复制代码
 SinglyLinkedList LinkedList = new SinglyLinkedList();
           LinkedList.addFirst(1);
           LinkedList.addFirst(2);
           LinkedList.addFirst(3);
          LinkedList.addFirst(4);
          LinkedList.addFirst(5);

           LinkedList.loop1(value->
                   {
                       System.out.println(value);
                   }
           );
        System.out.println("------------------------");
       LinkedList.loop2(value->
                {
                    System.out.println(value);
                }
        );
        //使用迭代器去遍历链表
        for (Integer value : LinkedList) {
            System.out.println(value);
        }
    }

二分查找

1.基础版

java 复制代码
 public  static String binarysearch(int []arr, int data)
    {//1.定义两个变量,一个站左边,一个站右边
        int left=0;
        int right=arr.length-1;
       while (left<=right)
       {
           int middle=(left+right)/2;
           if (data<arr[middle])
           {
              right=middle-1;
           }
           else if(data>arr[middle])
           {
               left=middle+1;
           }
           else {
               return  String.valueOf(middle);
           }
       }

        return "该数组没有该数字";
    }

1.关于为什么在中间值不使用middle=(left+right)/2;

因为Java中使用 在Java中,进行二分查找时,直接使用 middle = (left + right) / 2 可能会遇到整数溢出的问题。这是因为当 leftright 都非常大且接近 Integer.MAX_VALUE 时,它们的和可能会超过 int 类型的最大值,从而导致溢出。

改进版

int middle = left + (right - left) / 2;

无符号右移一位相当于除以2向下取整

那么

int middle = left +right >>> 1;

2.关于为什么使用left<=right去作为判断跳出循环的条件而不是去用left<right去作为判断的条件

如果使用 left < right 作为条件,那么在最后一次迭代中,当 left 和 right 相邻时(即 left = right - 1),循环就会提前终止,从而可能错过目标元素(如果它正好位于 left 或 right 指向的位置)。

left ==right 意味着它们指向的元素也会参与比较,left < right 只意味着middle指向的元素参与比较

2.改进版

核心为将right不作为比较元素

java 复制代码
public  static String binarysearch(int [] arr,int target){
         int left =0, right =arr.length;

         while (left<right)
         {
             int  middle =left+right >>> 1;
             if(target<arr[middle])
             {
                 right=middle;
             }
             else if(arr[middle]<target)
             {
                 left =middle +1;
             }
             else {
                   return  String.valueOf(middle);
             }
         }
         return "没有找到该数字" ;
    }

该写法的特点:

i,j 对应着搜索区间 [0,a.length)(注意是左闭右开的区间),i<j 意味着搜索区间内还有未比较的元素,j 指向的一定不是查找目标

思考:为啥这次不加 i==j 的条件了?

回答:这回 j 指向的不是查找目标,如果还加 i==j 条件,就意味着 j 指向的还会再次比较,找不到时,会死循环

1)令right为arr.length是确定right为数组边界外一值并不是数组中的值不希望right参与比较,在其中left是参加比较的值而right是不参与比较的值

使得在target在小于arr[middle]时令right=middle

2)关于循环跳出条件为left<right的原因

当查找的数没有在数组中时会进行死循环报错

3.平衡版

问题切入点:

l为总循环次数,当元素在最右边与最左边时的循环次数不一致。在最右边要进行2*l次比较

时间复杂度都为log(n)。

核心为进行两分支(if else),比较一次,在循环中只是缩小范围不进行返回值

注意判断循环条件为(left+1<right)

java 复制代码
  public  static String binarysearch(int [] arr,int target) {
                int left =0, right=arr.length;
                while (1<right-left)
                {
                    int middle =(left+right)>>>1;
                    if(target<arr[middle])
                    {
                        right=middle;
                    }
                    else {
                        left=middle;
                    }

                }
                if(arr[left]==target)
                {
                    return  String.valueOf(left);
                }
                else {
                    return  "没有该数字";
                }
    }

4.java版二分查找

java 复制代码
private static int binarySearch0(long[] a, int fromIndex, int toIndex,
                                     long key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            long midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

关于使用Java自身提供的区别在于返回值当查询不到数据时返回的是-(插入值+1)

return -(low + 1); // key not found.

eg 当我的数组为{1,23,45,77,123}时我查找12时就会返回-2(因为12拍数组第二所以-(1+1)=-2)

java 复制代码
 int arr2[]={12,14,123,434,56};
        System.out.println(Arrays.binarySearch(arr2, 13));

返回-2

5.leftmost与rightmost

实现查找重复数字中最左边序号

java 复制代码
  public static int binarySearchLeftMost(int arr[], int target) {
        int left = 0, right = arr.length - 1;
        int candidate = -1;
        while (left <= right) {
            int middle = (left + right) >>> 1;
            if (target < arr[middle]) {
                right = middle - 1;
            } else if (arr[middle] < target) {
                left = middle + 1;
            } else {
                //记录候选者位置
                candidate = middle;
                right = middle - 1; // 继续向左搜索
            }
        }
        return candidate;
    }

实现查找重复数字中最右边序号

区别:在边界处不同

// 继续向右搜索,直到找到最右边的位置

left = middle + 1;

java 复制代码
public static int binarySearchRightMost(int arr[], int target) {  
    int left = 0, right = arr.length - 1;  
    int candidate = -1;  
    while (left <= right) {  
        int middle = (left + right) >>> 1;  
        if (target < arr[middle]) {  
            right = middle - 1;  
        } else if (arr[middle] < target) {  
            left = middle + 1;  
        } else {  
            candidate = middle;  
            // 继续向右搜索,直到找到最右边的位置  
            left = middle + 1;  
        }  
    }  

6.几个名词

范围查询

查询x<4,0...leftmost(4) - 1

查询x ≤ 4,0...rightmost(4)

查询4 < x,rightmost(4) + 1 ... infty

查询4 ≤ x,leftmost(4) ... infty

查询4 ≤ x ≤ 7,leftmost(4) ... rightmost(7)

查询4 < x < 7,rightmost(4) + 1 ... leftmost(7) - 1

求排名:leftmost(target) + 1

target可以不存在,如:leftmost(5) + 1 = 6

target也可以存在,如:leftmost(4) + 1 = 3

求前任:leftmost(target) - 1

leftmost(3) - 1 = 1,前任a[1] = 2

leftmost(4) - 1 = 1,前任a[1] = 2

求后任:rightmost(target) + 1

rightmost(5) + 1 = 5,后任a[5] = 7

rightmost(4) + 1 = 5,后任a[5] = 7

求最近邻居

前任和后任距离更近者

练习题

二分查找简单

9.3 在排序数组中查找元素的第一个位置和最后一个位置

搜索插入位置

相关推荐
luoluoal几秒前
基于Spring Boot的装饰工程管理系统源码(springboot)
java·spring boot·后端
一个会的不多的人6 分钟前
C# Solidworks二次开发:宏录制实战讲解(第二讲)
开发语言·c#
J不A秃V头A16 分钟前
IDEA实用小技巧:方法之间的优雅分割线
java·intellij-idea
神奇夜光杯16 分钟前
Python酷库之旅-第三方库Pandas(181)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
tang138976426 分钟前
Python(包和模块)
开发语言·python
涛涛6号26 分钟前
PageHelper(springboot,mybatis)
java·spring boot·后端
nuomigege32 分钟前
普通变量和数组在大小端模式下的存储顺序考证
数据结构
ChoSeitaku36 分钟前
链表|反转链表|移除链表元素|链表的中间节点|返回倒数第k个节点|合并两个有序链表(C)
c语言·数据结构·链表
夜雨翦春韭38 分钟前
【代码随想录Day58】图论Part09
java·开发语言·数据结构·算法·leetcode·图论
Justinc.1 小时前
Flutter图片控件(七)
开发语言·flutter