1 / 58

第三章 向量

第三章 向量. 基本概念. 向量 扩展数组 向量的应用:矩阵( matrix). 程序的灵活性. 例:编程实现一个读取 n 个 字符串类型 数据的程序( 参见 C3_1.java ) Public static void main(String args[]) { ReadStream r = new ReadStream(); String v1,v2,v3,v4; v1 = r.readString(); v2 = r.readString(); v3 = r.readString();

thi
Download Presentation

第三章 向量

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第三章 向量

  2. 基本概念 • 向量 • 扩展数组 • 向量的应用:矩阵(matrix)

  3. 程序的灵活性 例:编程实现一个读取n个字符串类型数据的程序(参见C3_1.java) Public static void main(String args[]) { ReadStream r = new ReadStream(); String v1,v2,v3,v4; v1 = r.readString(); v2 = r.readString(); v3 = r.readString(); v4 = r.readString(); } 评注:要用n个字符串类型的变量去指向这n个字符串。

  4. 问题 • 略加改进(参见C3_2.java): • ReadStream r = new ReadStream(); • String data[]; • int n=4; • data=new String[n]; • for (int i=0;i<n;i++) • data[i]=r.readString(); • 评注:虽然通过数组建少了标识符的使用,但n需在编译时确定,程序仍然不够灵活。 • 能否有一种好的改进?如果n!= 4,n = 5,n= 6,n = 10000。该如何解决?

  5. 问题 • 极端做法:在编译前给n赋一个很大的值,如n = 1000000000。(参见C3_3.java) • 这样做可行吗? • int i=0; • int n=1000000000; • data=new String[n]; • for (r.skipWhite();!r.eof();r.skipWhite()) • { data[i]=r.readString(); • i++; • }

  6. 问题 • 进一步改进(参见C3_4.java): int n; n=r.readInt(); data=new String[n]; for (int i=0;i<n;i++) data[i]=r.readString(); • 要求用户在程序运行时确定n的大小。 • 把确定数组上限的负担转移给程序的用户身上。

  7. 问题 • 以上的实现存在的缺点: • 不灵活 • 浪费空间 • 负担转嫁到用户身上。 • 终极改进:我们需要一个灵活,不浪费空间,不增加用户负担的实现。这需要用到一种新的数据结构——向量(vector)。

  8. 向量 读取字符串的程序: public static void main(String args[]) { ReadStream r = new ReadStream(); Vector data; data = new Vector(); for( r.skipWhite();!r.eof();r.skipwhite()){ String s = r.readString(); data.add(s); } 程序并没有显示的跟踪data中存储的字符串的个数。

  9. 向量和数组初步对比 • 相似点: • 语义相似(new创建),向量的底层是数组。 • 都可以随机访问(可以按任意顺序访问)。 • 不同点: • 数组是静态的,而向量是动态的。 • 向量可以缩减增加,而数组不可以。

  10. 向量的接口(部分) • 从抽象列表继承而来;Vector extends AbstractList • 无参构造方法Vector(),默认向量的容量为10; • 含参构造方法Vector(int initialCapacity) • 在向量的末尾添加一个元素void add(Object obj) • 删除一个元素Object remove(Object element) • 查询向量的第?号元素Object get(int index) • 在向量的第?号位置插入一个元素void add(int index, Object obj) • 检查向量是否为空?boolean isEmpty() • 删除向量的第?号元素Object remove(int where) • 替换向量的第?号元素值Object set(int index, Object obj) • 向量的大小。int size()

  11. 字母频率 • 替代型密码的破译,字母频率 明文: how are you dong 替代加密 解密 密文: ipx bsf zpv epoh 字母p的频率为23.1%(3/13) • 统计学家对大量的文档统计后发现:每个字母在文档中出现的频率是一定的。 • 假设统计学家告诉我们:字母o的出现频率为23.1%。

  12. 字频率word-frequency • 作者的写作风格,单词的频率

  13. 设计思想 字符流r是否结束? N 读入单词 在字频向量中有记录否? Y 把新单词加入 字频向量, 频率置为1 N 逐个打印向量元素 Y 该单词频率加 1

  14. 向量的每一个元素类型都是Association (String,Integer) theKey theValue null null 1 null null 2 … … ... null null 999 null null 1000 vocab向量, 大小=0, 容量=1000 算法描述 • Vector vocab=new Vector(1000); • 字符流r:long long ago

  15. 向量的每一个元素类型都是Association (String,Integer) 向量的每一个元素类型都是Association (String,Integer) theKey theValue theKey theValue “long” 1 1 null null 1 null null 2 null null 2 … … ... … … ... null null 999 null null 999 null null 1000 null null 1000 vocab向量, 大小=1, 容量=1000 vocab向量, 大小=0, 容量=1000 字符流r:long long ago 1.读入第1个“long”

  16. 向量的每一个元素类型都是Association (String,Integer) 向量的每一个元素类型都是Association (String,Integer) theKey theKey theValue theValue “long” “long” 2 1 1 1 null null null null 2 2 … … … … ... ... null null null null 999 999 null null null null 1000 1000 vocab向量, 大小=1, 容量=1000 vocab向量, 大小=1, 容量=1000 字符流r:long long ago 2. 读入第2个“long”

  17. 向量的每一个元素类型都是Association (String,Integer) 向量的每一个元素类型都是Association (String,Integer) theKey theKey theValue theValue “long” “long” 2 2 1 1 “ago” null 1 null 2 2 … … … … ... ... null null null null 999 999 null null null null 1000 1000 vocab向量, 大小=1, 容量=1000 vocab向量, 大小=2, 容量=1000 字符流r:long long ago 3. 读入 “ago”

  18. for( r.skipwhite() ; !r.eof() ; r.skipWhite() ){ Association wordInfo; String vocabWord; String word = r.readString(); for( i=0; i < vocab.size(); i++) { wordInfo = (Association)vocab.get(i); vocabWord = (String)wordInfo.getKey(); if( vocabWord.equals(word) ){ Integer f = (Integer)wordInfo.getValue(); wordInfo.setValue(new Integer(f.intValue()+1)); break; } } if( i== vacab.size() ) vocab.add(new Association(word , new Integer(1))); }

  19. 向量类的实现 • 向量必须存储大量相似的记录,因此有一个Object类型的数组。 Object可以是字符串String(P30)、关联Association(P33)或者另外的向量Vector(P44)等。 • 向量应有一个描述当前向量大小(size())的整型数。 • 当向量的大小将超过容量(底层数组的长度:数组名.length),向量的容量就会增加。 • protected Object elementData[ ]; • protected int elementcount;

  20. 0 1 2 3 4 5 6 elementCount=0 向量的构造方法 public Vector() // post: constructs a Vector with capacity for 10 elements { this(10); // call one-parameter constructor } public Vector(int initialCapacity) { Assert.pre(initialCapacity >= 0,"Nonnegative capacity."); elementData = new Object[initialCapacity]; elementCount = 0; }

  21. 0 1 2 3 4 5 6 1 6 3 7 Index=1 elementCount=4 访问向量元素 *@pre0 <= index && index < size() *@post returns the element stored in location index public Object get(int index) { return elementData[index]; }

  22. 0 1 2 3 4 5 6 1 6 3 7 Index=1 elementCount=4 修改向量元素 *@pre 0 <= index && index < size() *@post element value is changed to obj; old value is returned public Object set(int index, Object obj) { Object previous = elementData[index]; elementData[index] = obj; return previous; }

  23. 0 1 2 3 4 5 6 1 6 3 7 4 5 9 0 1 2 3 4 5 6 1 6 3 7 elementCount=7 Index=1 elementCount=4 向量中增加元素 • 向量的尾部添加一个元素 • 向量的中间需要添加一个元素

  24. 0 1 2 3 4 5 6 1 6 3 7 obj elementCount=4 1、向量尾部添加元素 • 需要考虑的问题: • 向量的容量问题,如果向量的容量不够,就需扩展 @post adds new element to end of possibly extended vector public void add(Object obj) { ensureCapacity(elementCount+1); elementData[elementCount] = obj; elementCount++; } elementCount=5

  25. 0 1 2 3 4 5 6 7 9 2、向量中间增加元素 • 使用双参数add(int index, Object obj)方法,需要移动元素来产生一个空位,实现插入。例如:排队。 最初状态:

  26. 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 9 4 4 5 5 4 4 5 5 5 5 6 5 6 6 6 6 6 7 6 7 7 7 7 7 7 9 最初状态: 第1次后移: 第2次后移: 第3次后移: 第4次后移: 放置元素9:

  27. 思考 • 向量在中间插入元素时能否正向移动?会出现什么情况?

  28. 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 9 4 4 4 4 4 5 4 4 4 6 4 4 4 6 7 4 4 7 7 4 4 4 错误的做法: 9 最初状态: 第1次后移: 第2次后移: 第3次后移: 第4次后移: 放置元素9:

  29. 中间增加元素实现 public void add(int index, Object obj) { int i; ensureCapacity(elementCount+1); for (i = elementCount; i > index; i--) { elementData[i] = elementData[i-1]; } elementData[index] = obj; elementCount++; }

  30. 删除一个元素 • Object remove(int where) • 在指定位置上删除一个元素。 • 该元素其后得元素必须向前移动一位 • 向量中元素的个数减少一个 • 需返回where位置上的值。

  31. 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 5 4 5 5 5 6 5 5 6 6 7 6 7 6 6 7 7 7 7 最初状态: 第1次移动: 第2次移动: 第3次移动: 最后状态:

  32. 删除元素的实现 @pre 0 <= where && where < size() @post indicated element is removed, size decreases by 1 @param where The location of the element to be removed. public Object remove(int where) { Object result = get(where); elementCount--; while (where < elementCount) { elementData[where] = elementData[where+1]; where++; } elementData[elementCount] = null; // free reference return result; }

  33. 删除元素的另一种实现 public int remove( Object elem) { int nIndex; for( int i= 0 ; i< elementcount; i++){ if( elem.equals(elementData[i]){ remove(i); nIndex = i; } } return nIndex; }

  34. 判断向量是否为空、求大小 public int size() { return elementCount; } public boolean isEmpty() { return size() == 0; }

  35. 可扩展性 • 对于向量,我们仍然需要估计其初始大小。 public Vector(int initialCapacity) { Assert.pre(initialCapacity >= 0,"Nonnegative capacity."); elementData = new Object[initialCapacity]; elementCount = 0; capacityIncrement = 0; initialValue = null; } • 随着向量中元素的增长,向量空间可能不够,怎么办?

  36. 扩展方法1 • 每次增加一个元素的空间,并将原先的向量元素复制到新的向量中。 • 定量分析:假设:向量初始空间为1,每一次空间用尽,向量的空间加1,将原向量元素复制到新的向量中。求:当向量元素个数为n时,复制元素的操作进行了多少次?

  37. 复制的个数 扩容前 扩容后 1 0 0 2 0 1 0 1 3 0 1 2 0 1 2 4 0 1 2 3 0 1 2 3

  38. 原向量 新向量 复制的个数 1 2 1 2 3 2 … … … n-2 n-1 n-2 n-1 n n-1 C = 1 + 2 + … + n-1 = n(n-1)/2

  39. 假设:向量初始空间为0,每一次空间用尽,向量的空间加k,将原向量元素复制到新的向量中。求:当向量元素个数为n时,复制元素的操作进行了多少次?假设:向量初始空间为0,每一次空间用尽,向量的空间加k,将原向量元素复制到新的向量中。求:当向量元素个数为n时,复制元素的操作进行了多少次? • 分析:为了简单起见,在不影响结果的前提下,我们设:

  40. 复制的个数 扩容前 扩容后 0 2 0 1 0 1 4 0 1 2 3 0 1 2 3 K=2

  41. 原向量 新向量 复制的个数 0 k 0 k 2k k 2k 3k 2k … … … (s-2)k (s-1)k (s-2)k (s-1)k s*k =n (s-1)k C = 0 + k + 2k + … + (s-1)k = ks*(s-1)/2= n(n/k -1)/2=

  42. 扩展方法2 • 如果每次当原向量空间用尽时,扩展的新向量是原向量的2倍,则求:当向量元素个数为n时,复制元素的操作进行了多少次? • 设:k = log2n,即:

  43. 原向量 新向量 复制的个数 0 1 0 1 2 1 2 4 2 … … … 2k=n C = 0 + 1 + 2 + …+2k-1=2k-1=n -1

  44. 比较 • C1 = n(n-1)/2 ; C2 = n – 1 哪个更好?

  45. 向量扩展的实现 public void ensureCapacity(int minCapacity) { if (elementData.length < minCapacity) { int newLength = elementData.length; if (capacityIncrement == 0) { if (newLength == 0) newLength = 1; while (newLength < minCapacity) newLength *= 2; } else { while (newLength < minCapacity) newLength += capacityIncrement; } Object newElementData[] = new Object[newLength]; int i; for (i = 0; i < elementCount; i++) newElementData[i] = elementData[i]; elementData = newElementData; } }

  46. L系统 • 包含一个从字母表中得到的符号种子(又称起始字符串) • 一组改变或重写字符串的规则,称为生产 • L系统可以模拟简单生物体的生长过程

  47. 生产(重写规则)

  48. 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7 8 8 8 8 9 9 9 9 S L S L 最初状态 第0次 第1次 第2次

  49. 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 L S L S S L L L L S S L L L S L 0 1 2 3 4 5 6 7 8 9 10 11 12 S L L S L L S L S L L S L 第3次 第4次 第5次 第6次

  50. 实现(LSystem.java) public static Vector rewrite(Vector s) { Vector result = new Vector(); for (int pos = 0; pos < s.size(); pos++) { if (S == s.get(pos)) result.add(L); else if (L == s.get(pos)) { result.add(S); result.add(L); } } return result; }

More Related