算法第一章

基础编程模型

  • 递归

    • 递归总有一个最简单的情况—方法的第一条语句总是一个包含return的条件语句
    • 递归调用总是去尝试解决一个规模更小的子问题
    • 递归调用的父问题和尝试解决的子问题不应该有交集
  • 二分查找

    public int rank(int key,int[] a) {//数组必须是有序的
            //二分查找
            int lo = 0;
            int hi = a.length - 1;
            while (lo <= hi) {
                int mid = lo + (hi - lo) / 2;
                if (key < a[mid]) {
                    hi = mid - 1;
                } else if (key > a[mid]) {
                    lo = mid + 1;
                } else {
                    return mid;
                }
            }
            return -1;
        }
    

数据抽象

  • 对象:能承载数据类型的值的实体,有三大特性:状态(数据类型的值)、标识(将一个对象区别于另一个对象)、行为(数据类型的操作)

  • 但用例调用new()

    • 为新的对象分配内存空间
    • 调用构造函数初始化对象中的值
    • 返回该对象的一个引用,被变量关联
  • 使用引用类型的赋值语句将会创建该引用的一个副本。赋值语句不会创建新的对象,而只是创建另一个已经指向某个已经存在的对象的引用。这种情况被称为别名:两个变量同时指向一个对象。容易引起bug(按值传递

  • 字符串

    [外链图片转存失败(img-ALF8Hyh9-1562589069735)(C:\Users\z1664\AppData\Roaming\Typora\typora-user-images\1560926056140.png)]

  • 等价性:如果我们将想通类型的两个引用变量a和b进行等价性车市(a==b),我们检测的是其表示是否相同,即引用是否相同,即只有a和b关联了通一个对象的引用,a = = b才为true,如果我们想要检测两个对象的状态(数据类型的值)是否相等,需要重载equals()方法

背包、队列。栈

背包(Bag),队列(Queue),栈(Stack)是三种与对象的集合有关的基础数据类型,它们的区别在于删除或者访问对象的顺序不同

  • 背包:不支持从中删除元素,迭代的顺序不确定且于用例无关,使用Bag说明元素的处理顺序不重要

    public class Bag<item>
        Bag()  创建一个新背包
        void add(Item item)  添加一个元素
        boolean isEmpty() 是否为空
        int size() 元素数量
    
  • 队列:先进先出(FIFO)

    public class queue<item>
        Queue() 创建一个新队列
        enqueue()(入队)加入元素 
        dequeue()  (出队)除去元素 
        boolean isEmpty() 是否为空
        int size() 元素的数量
    
  • 栈:后进先出(LIFO)

     public class stack<item>
        stack()  创建一个新栈
        void push (入栈 ) 插入元素  
        item pop (出栈)除去元素
        boolean isEmpty() 栈是否为空
        int size() 栈中元素的数量
     
    
    
    import java.util.Iterator;
    
    /**
     * 可动态改变数组大小的下压栈的实现,可作为背包,队列等数据结构实现的模板
     */
    public class ResizingArrayStack<Item> implements Iterable<Item> {
        private Item[] a = (Item[]) new Object[1];//栈元素 java不支持泛型的数组
        private int N = 0;//元素数量
    
        public boolean isEmpty() {
            return N == 0;
        }
    
        public int size() {
            return N;
        }
    
        private void resize(int max) {//动态改变数组的大小,该方法私有,用户不必知道实现的具体细节
            //将栈移动到一个大小为max的新数组
            Item[] temp = (Item[]) new Object[max];
            for (int i = 0; i < N; i++) {
                temp[i] = a[i];
            }
            a = temp;
        }
    
        public void push(Item item) {
            //将元素添加到栈顶
            if (N == a.length) resize(2 * a.length);
            a[N++] = item;
        }
    
        public Item pop() {
            //从栈顶删除元素
            if (isEmpty()) {
                return null;
            } else {
                //return a[--N];直接返回 但会产生对象游离
                Item item = a[--N];
                a[N] = null;//N已经被减
                if (N > 0 && N == a.length / 4) resize(a.length / 2);
                return item;
            }
        }
    
        @Override
        public Iterator iterator() {
            return new ReverseArrayIterater();
        }
    
        private class ReverseArrayIterater implements Iterator<Item> {
            private int i = N;//以下方法对i进行操作而不改变N的值
    
            @Override
            public boolean hasNext() {
                return i > 0;
            }
    
            @Override
            public Item next() {
                return a[--i];
            }
        }
    }
    
    
  • 链表(与数组并列,分别对应链式存储和顺序存储)

    • 定义:一种递归的数据结构,它或者为空,或者是指向一个结点(node)的引用,该结点含有一个泛型的元素和一个指向另一条链表的引用。

      private class Node{//私有 不必为用例所知道
          Item item;
          Node next;
      }
      
    • 构造链表

      Node first = new Node();
      Node second = new Node();
      Node third = new Node();
      first.item = "to";
      second.item = "be";
      thir.item = "or";
      first.next = second;
      second.next = third;
      

      链表表示的是一列元素,所不同于数组之处,在于其增删更为方便

    • 在表头插入结点(所需时间和链表的长度无关)

      Node oldFirst = first;
      first = new Node();
      first.item = "not";
      first.next = oldFirst;
      
    • 在表头删除结点

      first = first.next;
      
    • 在表尾插入结点

      Node oldLast = lsat;
      last = new Node();
      last.item = "not";
      oldLast.next = last;
      
    • 实现任意插入和删除 需使用双向链表

      private class Node{
          Node previous;
          Node next;
          Item item;
      }
      
      //假设已有结点a,b,c,d,欲删除b结点
      b.previous.next = b.next;
      b.next.previous = b.previous;
      
      //假设已有a,c,d结点,欲在c之前添加b结点
      c.previous.next = b;
      b.next = c;
      b.previous = c.previous;
      c.previous = b;
      

import java.util.Iterator;

/**
 * 下压堆栈(链表实现)
 */
public class Stack<Item> implements Iterable<Item> {
    private Node first;//栈顶(最近添加的元素)
    private int N = 0;//元素数量

    private class Node {
        Item item;
        Node next;
    }

    public boolean isEmpty() {
        return N == 0;//或first==null;
    }

    public int size() {
        return N;
    }

    public void push(Item item) {
        //向栈顶添加元素
        Node oldfirst = first;
        first = new Node();
        first.item = item;
        first.next = oldfirst;
        N++;
    }

    public Item pop() {
        //从栈顶删除元素
        Item item = first.item;
        first = first.next;
        N--;
        return item;
    }

    @Override
    public Iterator<Item> iterator() {
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item> {
        private Node current = first;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public Item next() {
            Item item = current.item;
            current = current.next;
            return item;
        }
    }
}

import java.util.Iterator;

/**
 * 先进先出队列的链表实现
 */
public class Queue<Item> implements Iterable<Item> {
    private Node first;//最早添加的结点
    private Node last;//最晚添加的节点
    private int N;

    private class Node {
        Node next;
        Item item;
    }

    public boolean isEmpty() {
        return first == null;
    }//或:N==0

    public int size() {
        return N;
    }

    public void enqueue(Item item) {
        //向表尾添加元素
        Node oldlast = last;
        last = new Node();
        last.item = item;
        if (isEmpty()) {//链表为空,将first和last都指向新创建的结点
            first = last;
        } else {
            oldlast.next = last;
        }
        N++;
    }

    public Item dequeue() {
        //从表头删除元素
        Item item = first.item;
        first = first.next;
        if (isEmpty()) {//如果链表为空,将last更新为null
            last = null;
        }
        N--;
        return item;
    }

    @Override
    public Iterator<Item> iterator() {
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item> {
        private Node current = first;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public Item next() {
            Item item = current.item;
            current = current.next;
            return item;
        }
    }
}
import java.util.Iterator;

/**
 * 背包(链表实现)
 */
public class Stack<Item> implements Iterable<Item> {
    private Node first;//链表首结点
    private int N = 0;//元素数量

    private class Node {
        Item item;
        Node next;
    }

    public boolean isEmpty() {
        return N == 0;//或first==null;
    }

    public int size() {
        return N;
    }

    public void add(Item item) {
        //和stack的push方法完全相同
        Node oldfirst = first;
        first = new Node();
        first.item = item;
        first.next = oldfirst;
        N++;
    }
//三种数据结构对于迭代的实现相同 但stack和queue的访问顺序不同
    @Override
    public Iterator<Item> iterator() {
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item> {
        private Node current = first;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public Item next() {
            Item item = current.item;
            current = current.next;
            return item;
        }
    }
}

算法分析

  • 执行最频繁的指令(内循环)决定了程序运行的总时间
  • 内存:(内存的使用会被填充为8字节的整数倍)
    • 对象
      • 实例变量
      • 对象本身的开销(一般为16字节):
        • 一个指向对象的类的引用(内存地址 占用8个字节)(不包含其所指的对象占用的内存)
        • 垃圾收集信息
        • 同步信息
    • 链表 嵌套有内部类,还需额外的8字节(用于一个指向外部类的引用),所以一个Node对象占用40字节(16字节的对象开销、8字节的额外开销、对item和node对象的引用各占用8字节)
    • 数组
      • 头信息 24字节
        • 对象开销16字节时
        • 长度记录 4字节
        • 填充4字节
      • 两类情况:
        • 保存的是原始数据类型 则占用长度乘以数据类型所占用的字节数之积个字节, 如int[N]占用(24+4N)个字节(会被填充为8字节的倍数)
        • 保存的是对象的引用,此部分内存占用引用所占的内存与对象所占用的内存之和
    • 字符串对象 40字节(字符所占用的内存另计)(String类型的char数组常常在多个字符串之间共享)
      • 16字节表示对象
      • 数组引用8字节
      • 3个int实例(偏移量、长度、散列值)12字节
        • 同步信息
    • 链表 嵌套有内部类,还需额外的8字节(用于一个指向外部类的引用),所以一个Node对象占用40字节(16字节的对象开销、8字节的额外开销、对item和node对象的引用各占用8字节)
    • 数组
      • 头信息 24字节
        • 对象开销16字节时
        • 长度记录 4字节
        • 填充4字节
      • 两类情况:
        • 保存的是原始数据类型 则占用长度乘以数据类型所占用的字节数之积个字节, 如int[N]占用(24+4N)个字节(会被填充为8字节的倍数)
        • 保存的是对象的引用,此部分内存占用引用所占的内存与对象所占用的内存之和
    • 字符串对象 40字节(字符所占用的内存另计)(String类型的char数组常常在多个字符串之间共享)
      • 16字节表示对象
      • 数组引用8字节
      • 3个int实例(偏移量、长度、散列值)12字节
      • 4个填充字节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值