数据结构

50 阅读4分钟

面试题

单向链表:实现单向链表元素的反转

  • 先定义一个新节点reverseHead = new HeroHead()
  • 从头到尾遍历原来的元素,没遍历一个节点,将其取出,并放到新的链表reverseHead()的最前端
  • 原来链表的head.next = reverseHead
  • 只需要一个辅助节点
  • 注意:先指针下移,再复制元素
public static HeroNode reverse(HeroNode head) {
    //TODO 如果当前链表为空,或
    HeroNode reverseHead = new HeroNode(0,"","");
    HeroNode next = head.next;
    while (true) {
        if (next == null) {
            break;
        }
        HeroNode node = next;
        //next先指针下移,再复制元素
        next = next.next;
        node.next = reverseHead.next;
        reverseHead.next = node;
    }
    head.next = reverseHead.next;

    return head;
}

单向链表与双向链表的使用区别

  • 添加:单向链表添加,指针找到添加元素的前一个节点,双向链表指针找到当前节点

双向链表

-双向链表的操作,temp都是指向当前节点

  • 添加:先找到双向链表的最后一个节点
temp.next = newHeroNode
newHeroNode.pre = temp
  • 删除
  • 删除节点要注意是否是最后一个节点!!!
temp.next=temp.next.next;
temp.next.pre = temp.pre
  • 修改:对于链表的修改,需要找到指定的元素,所以可以定义一个flag

可以用数组实现栈,也可以用链表实现栈

  • 使用数组保存栈的元素
  • 初始化top = -1
  • 入栈:top++; stack[top] = value
  • 出栈:int value = stack[top]; top--; return value

栈满,栈空

1.png

入栈,出栈

1.png

使用栈实现计算器

public class Calculator {
    public static void main(String[] args) {
        String expression = "3+2*6-2";
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(20);

        //定义需要的相关变量
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';//将每次扫描的char保存到ch
        //while循环遍历expression
        while (true) {
            ch = expression.substring(index,index+1).charAt(0);
            if (operStack.isOper(ch)) {
                if (operStack.isEmpty()) {
                    operStack.push(ch);
                }else {
                    if (operStack.priority(ch) >= operStack.priority(operStack.peek())) {
                        operStack.push(ch);
                    }else {
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1,num2,oper);
                        numStack.push(res);
                        operStack.push(ch);
                    }
                }
            }else {
                numStack.push(ch-48);
            }
            index++;
            if (index >= expression.length()) {
                break;
            }
        }
        //当出了循环的时候,操作符栈里面的元素都是 + -,高优先级的已经被计算过了
        while (true) {
            if (operStack.isEmpty()) {
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            numStack.push(res);
        }
        int res2 = numStack.pop();
        System.out.println("表达式" + expression + "="+res2);

    }

}
class ArrayStack2{
    private int maxSize;
    private int[] stack;
    private int top = -1;

    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    public int[] getStack() {
        return stack;
    }

    public void setStack(int[] stack) {
        this.stack = stack;
    }

    public int getTop() {
        return top;
    }

    public void setTop(int top) {
        this.top = top;
    }

    public ArrayStack2(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.maxSize];
    }
    //栈满
    public boolean isFull() {
        return top == maxSize -1;
    }

    public int peek() {
        return stack[top];
    }
    //栈空
    public boolean isEmpty(){
        return top == -1;
    }

    //入栈
    public void push(int value) {
        if (isFull()) {
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top] = value;
    }
    //出栈
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空,没有数据");
        }
        int value = stack[top];
        top--;
        return value;
    }

    //遍历栈
    public void list() {
        while (!isEmpty()) {
            System.out.println("stack[" + top + "]" +"=" +stack[top]);
            top--;
        }
    }
    public void list1() {
        if (isEmpty()) {
            System.out.println("栈空,没有数据~");
            return;
        }
        for (int i = top; i >=0 ; i--) {
            System.out.print("stack[" + i+"]" + "="+stack[i]);
        }
    }
    //判断是不是一个运算符
    public boolean isOper(char val) {
        return val == '+' || val == '-' || val == '*' || val == '/';
    }
    //返回运算符的优先级
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        }else if (oper == '+' || oper == '-') {
            return 0;
        }else {
            return -1;
        }
    }

    //计算方法
    public int cal(int num1,int num2,int oper) {
        int result = 0;
        switch (oper) {
            case '+':
                result = num1 + num2;
                break;
            case '-':
                result = num2 - num1;
                break;
            case '*':
                result = num2 * num1;
                break;
            case '/':
                result = num2 / num1;
                break;
            default:
                break;
        }
        return result;

    }
}

问题:当输入为70的时候,不能辨别为两个数字

  • 不能在扫描是数字的时候就直接入栈
  • 需要再向后面扫描一位,直到不是数字时,再进行拼接入栈

排序算法

冒泡排序

  • 一次比较相邻的两个元素,并交换
  • 每个大循环,会排除一个顺序
  • 优化:当有一个大循环没有发生交换时,代表已经有序,break,退出后面的其他大循环
  • 一个冒泡排序最少执行元素-1次,还是在全是相同的情况下

1.png

3.png 优化的冒泡排序

1.png

int[] a = new int[]{1,1,1,1,1,1,1,1,1};

boolean flag = false;
for (int i = 0; i < a.length - 1; i++) {
    for (int j = 0; j < a.length - i - 1; j++) {
        if (a[j] > a[j+1]) {
            flag = true;
            int temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
        }
    }
    if (!flag) {
        break;
    }else {
        //TODO 在一次大排序中可能交换过,flag = true
        //TODO 所以需要重置flag = false
        flag = false;
    }
}

选择排序

  • 第一轮将最小的元素放在第一位
  • 第二轮将其他最小的放在第二位
  • 每次只交换两个位置

1.png

1.png

int[] a = new int[]{101,34,43,32,1,65,21,119,1};
//101 34 119 1
//1 34 119 101

int index = 0;
int min = 0;
for (int i = 0; i < a.length; i++) {
    index = i;
    min = a[i];
    for (int j = i+1; j < a.length ; j++) {
        //TODO 这里不像冒泡排序,没有往后看一位,且是i开头,不需要a.length-i
        //TODO 且本身就不会越界
        if (min > a[j]) {
            min = a[j];
            index = j;
        }
    }
    a[index] = a[i];
    a[i] = min;
}

插入排序

  • 将数据分成两组,从后往前遍历,比它小则后移,比他大则定位

1.png 2.png

//{101,34,52,1}  ==>  {34,101,1,12} ==> {1,34,101,12}
int[] arr = new int[]{34,101,1,12,543,13,53,31,34,31,4234};
for (int i=1;i<arr.length;i++) {

    int insertVal = arr[i]; //1
    int insertIndex = i - 1;//1
    while (insertIndex >= 0 && insertVal < arr[insertIndex]) { //index=0 1 <  a[0]
        arr[insertIndex + 1] = arr[insertIndex]; // a[1] = a[0] = 34
        insertIndex--;//index--=0-1=-1;结束了 //1-1=0; //34,101,101  //0-1=-1   34,34,101
    }
    arr[insertIndex + 1] = insertVal;  //a[0] = 1;

}
System.out.println(Arrays.toString(arr));

希尔排序

1.png

int[] a = new int[]{8,9,1,7,2,3,5,4,6,0};
int temp = 0;

for (int gap = a.length/2;gap > 0 ;gap/= 2) {
    for (int i = gap; i < a.length; i++) {
        for (int j = i - gap; j >= 0; j -= gap) { //i=7  j=7-5=2
            if (a[j] > a[j + gap]) {
                temp = a[j];
                a[j] = a[j + gap];
                a[j + gap] = temp;
            }
        }
    }
}

线性查找

就是遍历数组元素,最基本的查找

public static int search(int[] a,int num) {
    boolean flag = false;
    for (int i = 0; i < a.length; i++) {
        if (a[i] == num) {
            flag = true;
            System.out.println("找到了,下标为"+ i);
            return i;
        }
        if (i == a.length -1) {
            System.out.println("找不到");
            return -1;
        }
    }
    return -1;

}

二分查找

二分查找是查找算法,需要传入一个需要查找的数

  • int mid = (left + right) /2
  • 二分查找的前提是:数组元素是有序的
  • 递归调用:要知道什么时候结束递归
  • 退出递归的条件,找到了如何退出,找不到如何退出
  • 定义flag变量,找到更改,找不到不更改,最后根据是否更改flag,判断是否找到该元素 1.png
public static void binarySearch(int[] a,int num) {
    int left=0;
    int right = a.length-1;
    boolean flag = true;
    while (left <= right) {
        int middle = (left + right) / 2;
        if (num == a[middle]) {
            System.out.println("找到了,位置为" + middle);
            flag = false;
            break;
        }else if (a[middle] > num) {
            right = middle - 1;
        }else {
            left = middle + 1;
        }
    }
    if (flag) {
        System.out.println("找不到");
    }
}