私たちが勉強したい最初のデータ構造は、配列、配列多くの価値が採掘です。
配列の基礎
記憶のための行のデータ・シンボル
配列のインデックスは0から始まり、配列要素の同じタイプを格納するために必要なJava構文は、あなたは括弧内の被写体で進んで要素を取得することができます。
これのいくつかは、Mainメソッドを見ることができます。
パッケージcn.mtianyan。
パブリッククラスメイン{
public static void main(String[] args) {
// 必须传入长度
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
System.out.println("=========");
int[] scores = new int[]{100,99,96};
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
System.out.println("=========");
for (int score : scores) {
System.out.println(score);
}
System.out.println("=========");
scores[0] = 92;
for (int score : scores) {
System.out.println(score);
}
}
}
それはインタフェースが横断することができます実装しているため配列は、これを通過することができます。
私たちは、そのインデックスの非常に重要な部分であるのJavaの配列を提供します。セマンティックインデックスはや意味ないかもしれないが、2は学校を表すことができるかもしれませんが、また無セマンティクスとみなすことができます。
アレイの最大の利点:高速クエリ、[2]直接番号2のインデックスにスコア。したがって、最良の配列は、「セマンティックインデックス」、生徒の学力の学生番号2の検索に適用されます。
しかし、すべてではないのような×××数、ケーブルの弓のセマンティクスは、アレイに適用されます:1101031985121 66666;メモリ空間の多くを開くことでしたインデックス、続く、スペースを無駄にしました。
配列はまた、「何の意味的なインデックス」状況に対処することはできません。この章で私たちの「何の意味的なインデックスありません」、状況に対処するために、アレイの主な用途。
インデックスは、配列のサイズは、容量が8である、3 :?素子の容量及び大きさに差がないことをどのように、意味論ではありません。
要素を追加する方法の要素(ノルウェービット)を削除する方法?(どのサイズ以上に対処するため)?Javaの配列にこれらの方法なしに、我々は、Javaベースの配列、第二のパッケージ当社独自の配列クラスを必要としています。
私たちは、動的配列(内部はまだ達成するためのJava配列を使用)、Javaは静的な配列であるが所有しています。
CRUDは、いくつかの4つのデータ構造は必ずしも完全ではありません。容量は、アレイは、最大保持することができ、そして多くの実用的な要素が関連していない保持することができますどのように多くの要素です。
パッケージcn.mtianyan。
パブリッククラスArray {
プライベートINT []データ。
プライベートint型のサイズ。
/**
* 带容量参数构造函数
*
* @param capacity 数组容量
*/
public Array(int capacity) {
data = new int[capacity];
size = 0;
}
/**
* 默认构造函数
*/
public Array() {
this(10);
}
/**
* 静态数组入参构造函数
*
* @param data 传入静态数组
*/
public Array(int[] data) {
this.data = data;
}
/**
* 获取数组元素个数
*
* @return size 数组元素个数
*/
public int getSize() {
return size;
}
/**
* 获取数组的容量
*
* @return capacity 获取容量
*/
public int getCapacity(){
return data.length;
}
/**
* 判断数组是否为空
*
* @return 是否为空
*/
public boolean isEmpty(){
return size == 0;
}
}
配列に要素を追加
配列の最後に要素を追加します。
最初の空の位置のデータサイズを指します。あなたは要素を追加するときにのみ、[サイズ] ARRに追加する必要があり、サイズは++することができます。(添加元素完全宣告注)
/**
* 向所有元素末尾添加一个新元素。
*
* @param e 添加的元素
*/
public void addLast(int e){
//もし(isFull()){
//( "AddLastが失敗した配列がいっぱいになった。")新しいIllegalArgumentExceptionをスローし、
//}他{
//データ[サイズ] = E。//データ【サイズ++] = E。
//サイズ++;
//}
(サイズ、E)を加えます。
}
/**
* 向索引0号位置添加元素
*
* @param e 添加的元素
*/
public void addFirst(int e){
add(0,e);
}
/**
* 在index位置插入一个新元素e
*
* @param index 插入位置索引
* @param e 插入元素
*/
public void add(int index,int e){
if (isFull())
throw new IllegalArgumentException("AddLast failed. Array is Full");
// 大于size就不是紧密排列了
if (index<0 || index>size){
throw new IllegalArgumentException("AddLast failed. Required index<0 or index>size ");
}
else {
// 从哪开始挪呢: 从size-1这个元素(size本身是指向空的),挪到哪个呢,index位置的这个元素也是要挪的。
for (int i=size-1; i>=index; i--){
data[i+1] = data[i]; // 后一个等于前一个,从数组最后一个元素开始。
// 极端值验证: size 1 index 0;(i=0;i>=0;i--)会执行一次data[1]=data[0].正确。
}
data[index] = e;
size++;
}
}
77は後方に移動すべきである要素の後ろに挿入されています。
注それ以外の場合は、値の範囲を作成し、後方に移動し、ここからここでは100でなければなりません。
配列の要素の要素を照会および変更
/**
* 打印数组信息及遍历元素。
*
* @return 数组信息和元素遍历结果
*/
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d, capacity = %d\n",size,data.length));
res.append('[');
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i !=size-1) // 最后一个元素不要加,
res.append(", ");
}
res.append(']');
return res.toString();
}
パッケージcn.mtianyan。
パブリッククラスArrayTest {
パブリック静的無効メイン(文字列[] args){
配列array =新しいアレイ(20)。
(I 0 = int型、iが10 <; Iは++){ため
array.addLast(I)。
}
のSystem.out.println(アレイ)
}
}
array.add(1,100);
System.out.println(array);
array.addFirst(-1);
System.out.println(array);
一つの要素を削除してください。
/**
* 传入索引,获取该位置元素
*
* @param index 要获取的元素索引
* @return 返回该位置数组元素
*/
public int get(int index){
if (index<0 || index>=size){
throw new IllegalArgumentException("Get failed. Required index<0 or index>=size ");
}else {
return data[index];
}
}
取得方法は、配列の後半は保証パッケージを介して、空間に使用されていないにユーザが使用することはできません。
System.out.println(array.get(array.getSize() - 1))。
/**
* 传入索引和元素值,将该位置元素设置为传入值
* @param index 索引
* @param e 传入值
*/
public void set(int index,int e){
if (index<0 || index>=size){
throw new IllegalArgumentException("Set failed. Required index<0 or index>=size ");
}else {
data[index] = e;
}
}
array.set(0,-99);
System.out.println(array);
配列は要素を検索し、削除し、含まれてい
あなたは88の開始の後ろから、要素1のインデックスを削除したい場合は、前進サイズインデックスの時間を移動する必要があります。のみsize--、他の操作を行うことなく、要素の最後の位置のために、ユーザーがアクセスできないため。
/ **
- 要素eがアレイが発見されています
- @paramと
-
包含真@return。不包含偽
* /
パブリックブール含有(int型E){
ため(; iはサイズ<; I = 0 int型私は++){
IF(データ[I] == E){
trueを返します。
}
}
falseを返します。
}/ **
- 配列内の要素を見つけ、そのインデックス(最初の出会い)を返します
- @param電子要素
-
@return不-1存在。存在I
* /
公共int型の検索(int型E){
ため(; iはサイズ<; I = 0 int型私は++){
IF(データ[I] == E)は{
リターンI。
}
}
-1を返します。
}公共のint []のfindAll(INT E){
INT [] tempArray =新しい新しいINT [サイズ];
int型のインデックス= 0;
のために(INT I = 0; Iサイズ<; Iは++){
IF(データ[I] == E) {
tempArray [インデックス] = I;
インデックス++;
}
}
INT [] IndexArray =新しい新しいINT [インデックス];
{(; Iは、インデックスを<I ++はI = 0をINT)ため
IndexArray [I] = tempArray [I]は;
}
IndexArrayを返します;
}
tempArray効果を用いるのfindAll一時オブジェクトを注:int配列は、その大きさとは一緒に渡されています。System.out.println("===============加入重复元素后数组如下==================="); array.add(3,3); array.add(array.getSize()-1,9); System.out.println(array); System.out.println("================包含 寻找测试==========================="); System.out.println(array.contains(-99)); System.out.println(array.contains(-100)); System.out.println("3的index: "+array.find(3)); int [] tmpArr = array.findAll(3); for (int i : tmpArr) { System.out.print(i+" "); } System.out.println();
/ **
- 配列要素を削除し、削除された要素の値を返します
- @paramインデックスインデックス
-
インデックス値@return要素の位置
* /
パブリックINT削除(INTインデックス){
//判断空、空の配列removeFirstとインデックス= 0、サイズ= 0 、インデックス> =サイズ異常。空の配列removeLastインデックス= -1インデックス<0異常;
IF(インデックス<0 ||インデックス> =サイズ){
スロー新しい新しいIllegalArgumentExceptionが( "必要なインデックスを削除する<0またはインデックス> =サイズに失敗しました。");
}他{
int型RETデータ= [インデックス];
のための(INT I =インデックス+ 1; Iサイズ<; Iは++){
//要素の開始は、インデックス位置、要素を移動その端部の要素から始まり、そこから移動し、移動し、サイズ1 (このように等しくない記号)
DATA [-I 1] =データ[I]; //後に事前に等しい
}
size--、
リターンRET;
}
}/ **
- 最初の(インデックス0)要素を削除
-
@return削除要素値
* /
パブリックINT removeFirstと(){
リターン削除(0);
}/ **
- 最後の(インデックスサイズ-1)要素を削除
-
削除された要素の値を@return
* /
パブリックint型removeLast(){
リターン削除(-size 1);
}/ **
- アレイ内の特定の要素の値を削除する(配列が最初に見つかった削除します)
- @param E要素値
-
@return成功を削除
* /
パブリックブールremoveElement(E int型){
int型のインデックスは=(E)を検索し、
IF {(インデックス= -1!)
(インデックス)を削除し、
trueに戻る;
}
falseに復帰;
}/ **
- 配列値に含まれるすべての要素を削除します
- @param E要素値
-
@return成功の削除
* /
パブリックブールremoveAllElement(E int型の){
int型[] = IndexArrayのfindAll(E);
(!indexArray.length = 0){IF
のために(I = 0 int型; I <indexArray.length; I ++を){
削除(indexArray [I] -i) ; //独創的-iことに注意してください
}
; trueに戻り
}
偽に戻り;
}
削除重鎖および困難コード(indexArray [I] -i)は 、 ここにある可能性が高いです異常原因、無視。ようにと、2 -2の近くにあるべきである;各アレイが1つを削除するため、本来の要素のインデックス値に近づいて取得した後、-1であるべきです。System.out.println("================开始删除测试========================"); System.out.println(array); array.remove(3); // 删除一个3 System.out.println(array); array.removeElement(1); // 删除1 System.out.println(array); System.out.println("=====删除index3 后 删除元素1如上===="); array.removeAllElement(9); System.out.println(array); System.out.println("=====删除所有9====="); array.addFirst(-99); array.removeAllElement(-99); System.out.println(array); System.out.println("=====首位添加-99,后删除所有-99 结果如上====="); array.add(4,99); array.add(5,99); array.addFirst(99); array.addLast(99); array.add(7,99); System.out.println(array); System.out.println("=====上面为删除99前,下面为删除99后====="); array.removeAllElement(99); System.out.println(array);
ジェネリック医薬品を使用します
私たちは、上記の配列のみがint型を格納することができます実現しますが、私たちに必要なのは、様々なタイプの、さらにはカスタムコンテナオブジェクトを運ぶことができます。Javaのジェネリックは、この問題を解決することができます。
使用ジェネリックは:レッツデータ構造は、「任意の」データ型を配置することができ、基本的なデータ型にすることはできません、唯一のクラスのオブジェクトにすることができます。
Javaの基本的なタイプの8種類:ブール、バイト、文字、短い、int型、長い、フロート、ダブル;
ブール、バイト、CHAR、ショート、INT、ロング、フロート、ダブル:各基本データ型は、対応するパッケージを持っています。自動開梱、開梱。
Arrayクラスパブリック<要素>
パブリッククラスアレイ<E>は
ここで識別の種類をカスタマイズすることができ、クラス名の後にジェネリック型識別子に追加されます。
private E[] data;
ここでは、直接E.経由でインスタンス化することはできません だけにして間接的なオブジェクトによって変換。
データ=(E [])新しいオブジェクト[容量]。
/ **
- コンストラクタのパラメータに静的な配列
-
静的配列を着信@paramデータ
* /
パブリックアレイ(E []データ){
this.data =データ;
}
公共ボイド追加(INTインデックス、E E){
公共ボイドaddLast(E E){
E E公共ボイドaddFirst( ){
パブリックブール含有(INT E){
(INTのためにI = 0; Iサイズ<; Iは++){
IF(データ[I] .equals(E)){
trueに戻り;
}
}
;偽に戻り
}
ここで注意二つそのオブジェクト間のequalsメソッドの比較を使用するために、メンバーのアレイタイプに関連するすべてが変更されました。Eは、公開(INTインデックス){削除
//判断空、空の配列removeFirstとインデックス= 0、サイズ= 0 、インデックス> =サイズ異常。空の配列removeLastインデックス= -1インデックス<0異常;
IF(インデックス<0 ||インデックス> =サイズ){
スロー新しい新しいIllegalArgumentExceptionが( "必要なインデックスを削除する<0またはインデックス> =サイズに失敗しました。");
}他{
E RETデータ= [インデックス];
のための(INT I =インデックス+ 1; Iサイズ<; Iは++){
//要素の開始は、インデックス位置、要素を移動その端部の要素から始まり、そこから移動し、移動し、サイズ1 (このように等しくない記号)
DATA [-I 1] =データ[I]; //後に事前に等しい
}
size--;
DATA [サイズ] = NULL;
戻りRET;
}
}
DATA [サイズ]、さらにを指しだけでなく、ユーザー・アクセスの値は、新しい要素が自動的にカバー追加されます。ジェネリックを使用した後、配列に格納されたデータは、クラスオブジェクトへの参照であり、データ[サイズ]は依然として空間の放出に関連する問題にsize--、参照後の参照点であり、Javaは自動ガベージコレクション機構を有しているが、データなら[サイズ]まだ保存された参照は自動的にゴミをリサイクルすることはありません。データ【サイズ] = NULL;(必ずしも、それは新しいオブジェクトによって覆われていてもよいです)
nullに設定されていない場合は、不明なオブジェクトと呼ばれますが、復元されていないガベージコレクションメカニズムを使用していませんでした。不明なオブジェクト!=プログラムの最適化のためのメモリリーク、手動で削除より良いです。
での主な変更点
アレイ<整数> =新しい新しいArrayアレイ(20)と、
アレイ<整数>配列array =新しい新しい<>(20)と、
変更だけでなく、正常に動作することができない場合。しかし、我々はより良い、より明確種類で見られる追加したい、あなたはjdk1.7後に再び繰り返す必要はありません。
パッケージcn.mtianyan。
パブリッククラス学生{
private String name;
private int score;
public Student(String studentName, int studentScore){
name = studentName;
score = studentScore;
}
@Override
public String toString(){
return String.format("Student(name: %s, score: %d)", name, score);
}
public static void main(String[] args) {
Array<Student> arr = new Array();
arr.addLast(new Student("mtianyan1", 100));
arr.addLast(new Student("mtianyan2", 66));
arr.addLast(new Student("mtianyan3", 88));
System.out.println(arr);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return score == student.score &&
(name.equals(student.name));
}
@Override
public int hashCode() {
return Objects.hash(name,score);
}
ハッシュコードを書き直しや方法に等しいです。
System.out.println("================start=======================");
System.out.println(arr);
arr.remove(1);
Student stu1 = new Student("mtianyan1", 100);
Student stu3 = new Student("mtianyan3", 88);
arr.removeElement(stu1);
System.out.println(arr);
System.out.println("==============================================");
System.out.println(arr);
arr.removeAllElement(stu3);
System.out.println(arr);
動的配列
Javaの静的アレイ容量は、固定された、制限されています。私たちは、スケーラブルにそれを変換したいです。
十分ではない、それは二回、元の(4が8になります)、その後、元の配列の内容がコピーされます(サイズ変更)新しいアレイの容量をされて開け、最終的なデータは、新しい配列を指すようになります。ここでは、各値を乗り越えるために横断しなければならない、それが消費のパフォーマンスのためではない、2は、この章で後述します。
if (isFull())
throw new IllegalArgumentException("AddLast failed. Array is Full");
// 大于size就不是紧密排列了
if (index<0 || index>size){
throw new IllegalArgumentException("AddLast failed. Required index<0 or index>size ");
}
それでも違法な座標例外をスローしますが、完全な場合は、私たちの動的配列は、例外をスローしません。
if (isFull())
resize(2*data.length);
ここでは2倍、それは固定サイズの値の拡大に優れています。オプション2は、私たち自身の決断、1.5で選択されたコレクションのArrayListです。
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
パッケージcn.mtianyan。
パブリッククラスArrayTestDynamic {
パブリック静的無効メイン(文字列[] args){
アレイ<整数>配列=新しいアレイ<>();
(I 0 = int型、iが10 <; Iは++){ため
array.addLast(I)。
}
のSystem.out.println(アレイ)
array.addFirst(99)。
System.out.println(アレイ)
array.addLast(100)。
System.out.println(アレイ)
}
}
圧縮アレイの容量を削除します。
public E remove(int index){
// 判空,空数组 removeFirst index=0,size=0,index>=size异常。空数组 removeLast index=-1 index<0异常;
if (index<0 || index>=size){
throw new IllegalArgumentException("Remove failed. Required index<0 or index>=size ");
}else {
E ret = data[index];
for (int i=index+1;i < size;i++){
// 从哪个元素开始挪,从index位置的后一个元素开始,挪到哪个元素结束,挪到size-1(因此没等号)
data[i-1] = data[i]; // 前一个等于后一个
}
size--;
data[size] = null;
if (size == data.length /2 )
resize(data.length /2);
return ret;
}
}
ここでは、設定されたしきい値、アレイは容量の半分、半分空になったとき。
パッケージcn.mtianyan。
パブリッククラスArrayTestDynamic {
パブリック静的無効メイン(文字列[] args){
アレイ<整数>配列=新しいアレイ<>();
(I 0 = int型、iが10 <; Iは++){ため
array.addLast(I)。
}
のSystem.out.println(アレイ)
array.addFirst(99)。
System.out.println(アレイ)
array.addLast(99)。
System.out.println(アレイ)
array.removeAllElement(99)。
System.out.println(アレイ)
(I 0 = int型、iが5 <; Iは++){ため
array.removeElement(I)。
}
のSystem.out.println(アレイ)
}
}
シンプルな複雑さの分析
シンプルな時間複雑性解析の認識のPubMed:詳細な導出
(1)、(n)は、(LGN)、(nlogn)、O(N ^ 2)。
ビッグOは、実行時間と入力データとの関係を説明します
パブリックstatic int型SUM(INT [] NUMS){
int型SUM = 0;
(int型NUM:NUMS)は、SUM + = NUM
戻りSUM;
}
通常、我々は、アルゴリズム上のコードは、時間計算量はO(N)であると言うの。Nアルゴリズムと線形関係、直線方向での性能であり; nはNUMS内の要素の数です。
より多くのnum個の番号、長くアルゴリズムの実行時間。
ビッグOを使用する理由、O(n)と呼ばれますか?
私たちは、一定、リアルタイムT = C1の* nの+ C2を無視しているので、ビッグOは一般的であり、費やした時間のために、各番号は、int型のリターン合計時間は、c2がc1のです。
したがって、関係なく、C1の、c2は、彼らが直線的な関係を満たしてO(n)のアルゴリズムはどのくらいです。
偶数C1、C2は小さいが、二次であり、したがって、O(N 2)、O(N)の性能の差です。はい、理論的な性能の差が、のようなデータの小片についてはn = 10、むしろO(N2)より速く実行されます。
漸近的時間計算量は、nが無限大に行く場合を説明しました。(3000)高次アルゴリズムはローレベルが、一定の低いアルゴリズムよりも高速である:ソートアルゴリズムを進め、マージソート、クイックソート、小さなデータ挿入ソートに変換10~15%を得るために最適化され、小高次定数挿入ソートすることができます。
状況が無限大になるされているので、あなたは無視し、低次の項のいずれかになります。
ダイナミックアレイの時間複雑さの解析
操作を追加します。
addLastは、(E)// O(1 ) 末尾に追加、直接割り当て、他には何も行いませんします。
O(1)それは関係なく、配列のnは、addLastが一定時間内に完了することができるどのように大きな、私たちの配列の大きさとは関係がないことを意味し、その後、サイズの変更を考慮していません。
addFirst(E)// O(Nは ) 、前に追加されたすべての要素に移動された
n個の要素を、N-1は、時間の後に移動します。
(インデックス、e)の追加
および関連指標を。あなたが分析できるように:;平均してO(N / 2)である確率の指標を非常に多くの値が等しい、厳密な計算は、予想される時間を決定するために、確率論のいくつかの知識を必要とします
まとめるとアクションを追加し、全体ではO(n)のレベルです。
アルゴリズムの複雑さの解析では、多くの場合、いくつかの例外は、特殊なケースについて、次のセクションの話があり、最悪の場合に焦点を当てます。
// O(n)のレベルは、サイズ変更
操作を削除します。
操作を変更します。
セット(インデックス、E)// O (1) レベルの
ルックアップ操作:
(インデックス)// O(1)を取得
含まれていて、(E)// O(n)の
(E)// O(n)を見つけます
最もよく知られている場合は、配列のインデックスです。
操作の最後の要素場合、addLast RemoveLast O(1)再度割り当てを再反復するすべての要素のサイズを変更するように、O(n)のままです。これは、パフォーマンスの低下のサイズ変更操作のように見えます。
分析のために最悪のケースを使用することは無理があるサイズを変更。分析のための償却複雑さを使用して、次のセクション。
共有等しく複雑さと複雑さの衝撃を防ぎます
操作を追加します。
最悪の場合には、拡張に加えテールは、OからO(n)のレベルになる(1);私たちができない各操作サイズ変更、サイズ変更をトリガする前に10回。
電流容量= 8、及びaddLast使用される各加算演算を仮定する。
9 addLast操作は、サイズ変更トリガ、17の基本的な操作の合計を行いました。操作あたりのおおよその平均、基本的な操作を2回AddLast。
普及:と仮定容量= N、N + 1、CI addLast、リサイズトリガー、2Nの合計+ 1回基本的な動作は、各平均addLast操作、基本的な動作を二回。
これには、共有均等ダウン時間複雑度はO(1)レベルであり、各addLast操作平均時間にリサイズされます!そしてnは、それは問題ではありません。
この例では、そのようなの計算は最悪の場合の計算よりも等しく意味の共有します。共有均等に複雑償却時間複雑
同様に、我々はRemovelのASTの操作を参照して、複雑さはO(1)で償却
ショックの複雑。
私たちは、一人でremoveLast見て、addLastは、その複雑さはO(1)
しかし、容量の2つの重要なポイントは、一度削除を追加し、サイズを変更トリガを停止します。この場合の存在、問題の原因:あまりにも心配してremoveLastのサイズを変更する場合
(意欲)
ソリューション:レイジー
アレイのサイズは、電流容量の1/4容量が低下すると、その後の要素を追加するためのスペースがある1/2、1/4を減少させました。サイズ==容量/ 4、半分に容量のみ。
レイジー方法、怠惰な表情、アルゴリズムのより良いパフォーマンス。より怠惰な、アルゴリズムの性能(遅延複数のコード機構)を向上させる場合:セグメントツリーを挙げるバックの例があります。
if (size == data.length /4 && data.length/2 !=0)
resize(data.length /2);
それは整数除算であるため、長さが1である場合、これは、結果は0に等しく、新たな容量0アレイできません。