《算法》笔记

早就说要开始系统的学算法了,可总是被这个事那个事所拖累,从今天开始,认真拿着《算法》跟着coursera上的课程学算法。

1Union_Find判断问题:

算法的设计总是由API的设计开始,先将需要解决的问题抽象化,对需要的功能写出相应的API,然后在考虑解决这个问题用什么数据结构最好,最后再开始实现各个API。
关于Union_Find问题,判断两个对象之间是否联通的一个抽象的想法便是将union的对象放进同一个集合中,查询时若两个对象在一个集合中就是有路线可以联通。相应的我们就可以用数组来模拟对象所在集合,并且我们需要union,connected这两个简单的操作,还有一个构造函数读入对象。

<1>Quick-connect算法:很明显,算法的核心问题就是如何实现union和connected这两个API。快查算法的实现非常简单,初始化时给所有对象一个id,表示每个对象处于不同的集合。进行union操作时,如果id不同,就遍历整个数组将所有和其中一个对象id相同的对象的id都改成后一个对象的id。查询操作就会非常方便,只要比较两者id是否相同,即可判断是否联通。

<2>Quick-union算法:第一种实现方法最大的问题便是每次union都要遍历数组,效率太低,Quick-union算法是模拟树的实现,union时只要将根节点相连,整个集合就合二为一了。但这种问题带来了另外一个问题,那就是可能存在“瘦高”的树,那么去查询这种树的根节点将会非常耗费时间。

<3>Quick-union improvements:不过,我们完全可以优化上一算法来解决这个问题。增加一个数组变量来记录每个树的大小,当union时,总是将小树连接在大树的根节点下。这个看似小小的操作将大大提升运算效率,在大数据量下,会将平方级的运算提升至对数级。


华丽的分割线


2.Java栈与队列的实现:

栈和队列是两种相像的经典的数据类型,都是对数据添加删除做限制的线性表。其不同之处就在于出栈(pop)和出列(dequeue)的方式。栈是先进后出,队列是先进先出。

<1>Java虽说没有所谓的指针,但其实对于每个对象的引用就是指针,所以,栈可以用链表来实现:

public class Stack {
    Node first = null;
    private class Node {//定义内部类,实现节点的数据类型
        String value;
        Node next;
    }

    public boolean isEmpty() {//判空函数
        return first == null;
    }

    public void push(String item) {//入栈
        Node oldfirst = first;
        first = new Node();
        first.value = item;
        first.next = oldfirst;
    }

    public String pop() {//出栈
        String item = first.value;
        first = first.next;
        return item;
    }

    public static void main(String[] args) {
        Stack zhan = new Stack();
        System.out.println(zhan.isEmpty());
        zhan.push("hello");
        zhan.push("world!");
        System.out.println(zhan.pop());

    }

}

当然,栈也可以用简单数组实现:

public class Stack {
    String S[];
    int n = 0;//代表了栈顶

    public Stack(int capacity) {//要求客户端定义栈大小
        S = new String[capacity];
    }

    public boolean isEmpty() {//判空函数
        return n == 0;
    }

    public void push(String item) {//入栈
        S[n++] = item;
    }

    public String pop() {//出栈
        String item = S[--n];
        S[n] = null;//这两行代码是细节,如果直接返回S[--n]数组中对对象的引用还存在,可能会造成内存浪费。
        return item;
    }

    public static void main(String[] args) {
        Stack zhan = new Stack(2);
        System.out.println(zhan.isEmpty());
        zhan.push("hello");
        zhan.push("world!");
        System.out.println(zhan.pop());

    }

}

数组实现栈确实方便简单,但是它却必须规定大小,而在实际应用中我们的客户端往往不知道究竟需要多大的栈,所以我们可以对程序进行改进,动态改变数组大小:

public class Stack {
    String S[] = new String[1];
    int n = 0;

    public boolean isEmpty() //判空函数
    {
        return n == 0;
    }

    public void push(String item) //入栈
    {
        S[n++] = item;
        if( n == S.length )
            resize(2*S.length); 
    }

    public String pop() //出栈
    {
        String item = S[--n];
        S[n] = null;
        if( n == S.length/4)//如果判断是否等于S.length/2可能会出现“抖动”现象
            resize(S.length/2);

        return item;
    }

    private void resize(int capacity)//重规定数组大小
    {
        String copy[] = new String[capacity];
        for( int i = 0; i<n; i++ )
            copy[i] = S[i];
        S = copy;
    }

    public static void main(String[] args) {
        Stack zhan = new Stack();
        System.out.println(zhan.isEmpty());
        zhan.push("hello");
        zhan.push("world!");
        System.out.println(zhan.pop());

    }

}

这样就解决了数组实现栈大小限定的弊端。

对于两种不同的实现,我们应当发现,数组的基本操作要比链表更快,这就使得在处理数据时整体上是数组实现的栈效率更高。不过当数组进行动态伸缩的时候,操作时间会明显变长。所以可以根据实际需求选择实现方法。

<2>对于队列的实现,基本上和栈类似,但是链表实现我们要维护两个节点:first和last,enqueue时从last节点入队,dequeue时从first节点出队:

public class Queue {
    private Node first, last;

    private class Node{
        String value;
        Node next;
    }

    public boolean isEmpty()//判空函数
    {
        return first == null;
    }

    public void enqueue(String value)//从last节点入队
    {
        Node oldlast = last;
        last = new Node();
        last.value = value;
        last.next = null;
        if(isEmpty())
            first = last;
        else
            oldlast.next = last;
    }

    public String dequeue()//从first节点出队
    {
        String item = first.value;
        first = first.next;
        return item;
    }

    public static void main(String[] args) {
        Queue duilie = new Queue();
        System.out.println(duilie.isEmpty());
        duilie.enqueue("hello");
        duilie.enqueue("world!");
        System.out.println(duilie.dequeue());
    }

}

以下是数组实现:

public class Queue {
    private int first, last;
    String S[] = new String[1];


    public boolean isEmpty()//判空函数
    {
        return first == last;
    }

    public void enqueue(String value)//入队
    {
        S[last++] = value;
        if( last == S.length )
            resize(2*S.length);
    }

    public String dequeue()//出队
    {
        String item = S[first];
        S[first++] = null;
        if( last == S.length/4 )
            resize(S.length/2);
        return item;
    }

    private void resize(int size)//此处重定义数组比栈稍微复杂,需要找到first节点,将其重新归零 
    {
        String copy[] = new String[size];
        int j = 0;
        while( S[j] == null)
            j++;    
        for( int i = 0; i<last; i++ )
            copy[i] = S[i+j];
        S = copy;
    }

    public static void main(String[] args) {
        Queue duilie = new Queue();
        System.out.println(duilie.isEmpty());
        duilie.enqueue("hello");
        duilie.enqueue("world!");
        System.out.println(duilie.dequeue());
    }

}

数组实现多了一些栈不需要考虑的因素,关于两种实现方法的性能分析同栈。

(关于背包的实现,就是一个不可以取出的栈,除掉pop方法即可。)


华丽的分割线


3.初级排序算法

<1>选择排序

public class Selection_sort {

    public static void sort(int[] a)
    {
        for( int i = 0; i<a.length; i++ )
        {
            for( int j = i; j>=1; j-- )
            {
                if( a[j]<a[j-1])
                {
                    int t =a[j];
                    a[j] = a[j-1];
                    a[j-1] = t;
                }
            }
        }
    }

    public static void main(String[] args) {
        int a[] = {23,324,45,32,2,3,123,5,234,52,3,67,95};
        sort(a);
        for( int i = 0; i<a.length; i++ )
        {
            System.out.println(a[i]);
        }
    }

}

<2>插入排序

public class Insertion_sort {

    public static void sort(int[] a)
    {
        for( int i = 0; i<a.length; i++ )
        {
            for( int j = i; j<a.length; j++ )
            {
                if( a[j]<a[i])
                {
                    int t =a[j];
                    a[j] = a[j-1];
                    a[j-1] = t;
                }
            }
        }
    }

    public static void main(String[] args) {
        int a[] = {23,324,45,32,2,3,123,5,234,52,3,67,95};
        sort(a);
        for( int i = 0; i<a.length; i++ )
        {
            System.out.println(a[i]);
        }
    }

}

<3>希尔排序

public class Shellsort {

    public static void sort(int[] a)
    {
        int h = 1;
        while( h<a.length/3 )
            h = 3*h+1;
        while( h>=1)
        {
            for( int i = h; i<a.length; i++ )
            {
                for( int j = i; j>=h; j-=h )
                {
                    if( a[j]<a[j-h] )
                    {
                        int t = a[j];
                        a[j] = a[j-h];
                        a[j-h] = t;
                    }
                }
            }
            h = h/3;
        }
    }

    public static void main(String[] args) {
        int a[] = {23,324,45,32,2,3,123,5,234,52,3,67,95};
        sort(a);
        for( int i = 0; i<a.length; i++ )
        {
            System.out.println(a[i]);
        }
    }

}

华丽的分割线


4.归并排序

public class Mergesort {

    public static void sort(int[] a)
    {
        int item[] = new int[a.length];
        sort(a, item, 0, a.length-1);
    }

    private static void sort(int[] a, int[] item, int head, int tail)
    {
        if( head>=tail )
            return;
        int middle = (head+tail)/2;
        sort(a, item, head, middle);
        sort(a, item, middle+1, tail);
        merge(a, item, head, tail, middle);
    }

    private static void merge(int[] a, int[] item, int head, int tail, int middle)
    {
        for( int i = head; i<=tail; i++ )
            item[i] = a[i];
        int j = head;
        int k = middle+1;
        for( int i = head; i<=tail; i++ )
        {
            if( j>middle )
                a[i] = item[k++];
            else if( k>tail )
                a[i] = item[j++];
            else if( item[j]<=item[k] )
                a[i] = item[j++];
            else
                a[i] = item[k++];
        }
    }

    public static void main(String[] args) {
        int a[] = {23,324,45,32,2,3,123,5,234,52,3,67,95};
        sort(a);
        for( int i = 0; i<a.length; i++ )
        {
            System.out.println(a[i]);
        }
    }

}

自底向上的归并排序:

public class Mergesort {
    private static int item[];

    public static void sort(int[] a)
    {
        item = new int[a.length];
        sort(a, 0, a.length-1);
    }

    private static void sort(int[] a, int head, int tail)
    {
        for( int i = 1; i<a.length; i=i*2 )
        {
            for( int j = head; j<a.length; j=j+i+i )
            {
                merge(a, j, Math.min(a.length-1, j+i+i-1), j+i-1);
            }
        }
    }

    private static void merge(int[] a, int head, int tail, int middle)
    {
        for( int i = head; i<=tail; i++ )
            item[i] = a[i];
        int j = head;
        int k = middle+1;
        for( int i = head; i<=tail; i++ )
        {
            if( j>middle )
                a[i] = item[k++];
            else if( k>tail )
                a[i] = item[j++];
            else if( item[j]<=item[k] )
                a[i] = item[j++];
            else
                a[i] = item[k++];
        }
    }

    public static void main(String[] args) {
        int a[] = {23,324,45,32,2,3,123,5,234,52,3,67,95};
        sort(a);
        for( int i = 0; i<a.length; i++ )
        {
            System.out.println(a[i]);
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值