20172301 《Java軟件結構與數據結構》實驗一報告

20172301 《Java軟件結構與數據結構》實驗一報告

課程:《Java軟件結構與數據結構》
班級: 1723
姓名: 郭愷
學號:20172301
實驗教師:王志強老師
實驗日期:2018年5月30日
必修/選修: 必修java

一.實驗內容

實驗1:基礎鏈表創建

  • 經過鍵盤輸入一些整數,創建鏈表;這些數是你學號中依次取出的兩位數,再加上今天的時間。
  • 而後打印全部鏈表元素,並輸出元素的總數
  • 在你的程序中,請用一個特殊變量名來紀錄元素的總數,變量名就是你的名字。 例如你叫 張三, 那麼這個變量名就是nZhangSan
  • 作完這一步,把你的程序git push

實驗2:實現節點插入、刪除、輸出操做

  • 繼續你上一個程序, 擴展它的功能,每作完一個新功能,或者寫了超過10行新代碼,就git push。
  • 從磁盤讀取一個文件。這個文件有兩個數字。
  • 從文件中讀入數字1,插入到鏈表第5位,並打印全部數字,和元素的總數。保留這個鏈表,繼續下面的操做。
  • 從文件中讀入數字2, 插入到鏈表第 0 位,並打印全部數字,和元素的總數。 保留這個鏈表,並繼續下面的操做。
  • 從鏈表中刪除剛纔的數字1 並打印全部數字和元素的總數。

實驗3:實現鏈表的選擇排序

  • 使用冒泡排序法根據數值大小對鏈表進行排序;
  • 在排序的每個輪次中,打印元素的總數,和目前鏈表的全部元素。

實驗4:實現數組插入、刪除、輸出操做

  • 經過鍵盤輸入一些整數,創建數組。這些數是你學號中依次取出的兩位數。 再加上今天的時間。
  • 打印全部數組元素, 並輸出元素的總數。
  • 在你的程序中,請用一個特殊變量名來紀錄元素的總數,變量名就是你的名字。 例如你叫 張三, 那麼這個變量名就是 nZhangSan
  • 作完這一步,把你的程序git push
  • 實現數組插入、刪除、輸出操做
  • 從磁盤讀取一個文件。這個文件有兩個數字。
  • 從文件中讀入數字1,插入到數組第5位,並打印全部數字,和元素的總數。保留這個數組,繼續下面的操做。
  • 從文件中讀入數字2, 插入到數組第 0 位,並打印全部數字,和元素的總數。 保留這個數組,並繼續下面的操做。
  • 從數組中刪除剛纔的數字1 並打印全部數字和元素的總數。

實驗5:實現數組的選擇排序

  • 使用選擇排序法根據數值大小對數組進行排序
  • 在排序的每個輪次中,打印元素的總數,和目前數組的全部元素。

2.實驗過程及結果

實驗一:

  • 首先,要想創建一個鏈表,咱們須要有指針。須要新建一個指針類。這裏我並無直接套用以前用過的LinearNode類,而是新的Point類。
public class Point {
    protected int number;

    protected Point next = null;
    public Point(int num)
    {
        this.number = num;
    }
}
  • 而後,創建鏈表一樣須要須要插入和輸出方法。這裏的插入我一開始並無實現中間插入的,而是直接使用了尾插法。
public void add(Point element) {
        if ( head == null)
            head = element;
        else {
            Point temp = head;
            while (temp.next != null) {
                temp = temp.next;
            }
            temp.next = element;
        }

    }
  • 輸出就比較簡單了,遍歷一遍鏈表就行了。
public void PrintLinkedList()
    {
        Point node = head;
        while (node != null)
        {
            System.out.print(node.number + " ");
            node = node.next;
        }
    }
  • 最後是代碼測試類的截圖以及結果截圖。

這裏我一開始用的是String.spit(),有必定的侷限性,用戶輸入的時候必須輸入一個空格才能成功,在後面的數組實踐中已經更改,這裏便很少贅述。node

實驗二:

  • 根據題目要求,從磁盤中讀取文件,那麼確定會用到IO流。這裏要注意拋出IO異常。 根據咱們學習,IO流可使用FileReaderBufferedReaderFileInputStream,這裏我選擇的是BufferedReaderBufferedReader的有關問題,詳見問題一
  • 在實驗二中,我根據題目要求實現了在中間插入的方法和刪除方法。值得一提的是,起初的刪除方法我並無寫出循環中的else條件,致使了無限循環。詳見問題二
public void Insert(int x, Point element)
    {
        Point temp = head;
        if (x == 0)
        {
            element.next = head;
            head = element;
        }
        else {
            for (int y = 1; y < x - 1; y++) {
                temp = temp.next;
            }
            element.next = temp.next;
            temp.next = element;
        }
    }
public void DeleteNode(Point element)
    {
        Point ProNode = head, CurrentNode = head;
        while (CurrentNode != null)
        {
            if(CurrentNode.number != element.number)
            {
                ProNode = CurrentNode;
                CurrentNode = CurrentNode.next;
            }
            else{
                break;
            }
        }
        ProNode.next = CurrentNode.next;
    }
  • main方法的代碼截圖和代碼結果截圖。

實驗三:

  • 實驗三的重點也就是如何用鏈表實現冒泡排序而且如何在每一輪次中輸出鏈表和個數。
  • 首先,咱們要清楚冒泡排序的原理。由於涉及到三種排序,選擇排序,插入排序,冒泡排序。那麼我就在這列統一辨析一下。接下來的選擇排序便很少贅述。
    • 選擇排序:應該是最好理解的,最爲簡單直觀的排序方法,它每次都遍歷數組, 從中選擇出最大 (最小) 的元素,放在數組的第一位, 直到全部元素所有排序完成。 選擇排序在不一樣的場景下都會執行相同次數的遍歷,因此性能不是很高。它的時間複雜度是O (n^2) 。
    • 插入排序:原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。 若是數組原本就是有序的, 那麼此時的複雜度爲O (n) ;若是數組原本是倒序的,那麼插入排序的時間複雜度就爲O (n^2)。 插入排序較適應於元素少的數組進行排序。
    • 有個形象的圖來分析插入排序,鬥地主大部分人都玩過吧:
    • 冒泡排序:原理是將先後每兩個數進行比較,較大的數日後排,一輪下來最大的數就排到最後去了。而後再進行第二輪比較,第二大的數也排到倒數第二了,以此類推。它的最佳時間複雜度是O(n^2)。
  • 冒泡排序算法:
public void BubbleSort()
    {
        Point Node , CurrentNode;
        int temp;int nGuoKai = 0;
        for (Node = head;Node.next != null;Node = Node.next)
        {
            for (CurrentNode = head;CurrentNode.next != null; CurrentNode = CurrentNode.next)
            {
                if (CurrentNode.number>CurrentNode.next.number)
                {
                    temp = CurrentNode.number;
                    CurrentNode.setNumber(CurrentNode.next.number);
                    CurrentNode.next.setNumber(temp);
                    nGuoKai = length();
                }
            }
            PrintLinkedList();
            System.out.println();
            System.out.println("個數:" + nGuoKai);

        }
    }
  • 冒泡排序實現代碼中,由於冒泡排序須要進行兩個數的交換,因此咱們的指針類Point類須要有一個setNumber(int num)的方法。因此,我修改了Point類
public void setNumber(int number)
    {
        this.number = number;
    }
  • 在這個時候我是有疑問的,在每一輪中,輸出鏈表簡單,那麼如何輸出每次鏈表的個數。 鏈表不像數組,有固定的長度,那麼也意味着咱們沒法經過直接調用某個變量來計算鏈表的長度。因此,我給出了length()方法來計算鏈表的長度
public int length()
    {
        int length = 0;
        Point temp;
        temp = head;
        while (temp != null)
        {
            length++;
            temp = temp.next;
        }
        return length;
    }

其實也就是一個遍歷鏈表的過程。而後讓個人nGuoKai變量 = length(),即可以了。git

  • 實驗結果截圖:

實驗四:

  • 建立數組的實現很是簡單,這裏用戶交互的時候,我使用的是StringTokenizer類以彌補上一個鏈表實驗的不足。
    這裏直接給出測試類截圖:
  • 在數組的添加和刪除操做中,須要考慮數組內元素的移動。
public static int[] Insert(int[] nums, int position, int num)
    {
        // 對數組進行擴容
        int[] nums1 = new int[nums.length + 1];
        // 對數組進行擴容,不用新建一個數組。
        // nums = Arrays.copyOf(nums, nums.length + 1);
        //將原數組數據賦值給新數組
        for(int i = 0; i < nums.length; i++)
        {
            nums1[i] = nums[i];
        }
        //將大於position的數據向後移動一位
        for(int j = nums1.length-2;j>position - 1;j--)
        {
            nums1[j+1] = nums1[j];
        }
        //賦值到position位置
        nums1[position] = num;
        return nums1;
    }
  • 這裏的添加操做,我是先對數組的尾端進行操做。由於數組擴容了,因此此時的尾端應該是空的。
    • 因此我讓前一個元素來覆蓋後一個的元素。這樣並不會出現丟失元素的狀況。一直到我要插入的那個索引值position那裏,跳出循環,把要插入的值賦給position。最後返回數組。
    • 這裏在測試類測試時發生了問題。詳見問題三
    public static int[] Delete(int[]nums, int position)
      {
          // 對數組進行擴容
          int[] nums1 = new int[nums.length - 1];
          int i = 0;
          // 對數組進行擴容,不用新建一個數組。
          // nums = Arrays.copyOf(nums, nums.length - 1);
          //將原數組數據賦值給新數組
          for(int j = 0;j < nums.length ;j++)
          {
              if (position != j) {
                  nums1[i] = nums[j];
                  i++;
              }
          }
          //賦值到position位置
          return nums1;
      }
  • 刪除操做也一樣如此,把原來的數組從新複製到新的數組當中。當條件等於索引值時,就跳過便可。
  • 特別注意一點,像我註釋中所說的那樣,對數組進行擴容,不用新建一個數組。 咱們能夠直接使用Arrays類中的CopyOf()方法。固然它的實現也是基於新建一個數組來進行賦值。異曲同工。
  • 最後是測試類代碼和代碼結果截圖。

實驗五:

  • 選擇排序在上面也已經具體介紹過,並且上學期實現過數組的選擇排序。因此這裏很少介紹。
  • 數組的每一輪次的輸出和個數就要比鏈表的簡單了,由於數組的長度是固定的,而排序也並未改變數組的個數。
  • 這裏直接給出代碼和截圖。


3. 實驗過程當中遇到的問題和解決過程

  • 問題1:在思考IO流BufferedReader的時候,我記得須要有BufferedReader.close()的操做,來關閉輸入的流,釋放內存。
    可是我記得還有一個flush操做,彷佛是刷新緩衝區的,可是不記得具體的用法和用途了。 算法

  • 問題1解決方案:
    • 參考資料: FileWriter的flush問題
    • 首先,能夠得知,flush方法是用於BufferedWriter的操做,是爲了刷新緩衝區,使其寫入文件。
    • 一樣,FileOutputStream 繼承 OutputStream 並不提供flush方法的重寫。因此不管內容多少write都會將二進制流直接傳遞給底層操做系統,flush無效果,而Buffered系列的輸入輸出流函數。單從Buffered這個單詞就能夠看出他們是使用緩衝區的,應用程序每次IO都要和設備進行通訊,效率很低,所以緩衝區爲了提升效率,當寫入設備時,先寫入緩衝區,等到緩衝區有足夠多的數據時,就總體寫入設備。
      flush會刷新緩衝區,而close方法內部是會先調用flush方法再執行關閉方法的,殊途同歸。
  • 問題2:鏈表刪除操做,致使了無限循環。沒法進行後續的任何操做。
    如圖:
    數組

  • 問題2解決方案:
    • 由圖可見,我左邊的滾動條已經拉到最低了。那麼首先,要清楚問題出現的位置。這裏一看就是刪除中的操做陷入了無限循環,條件沒有跳出,致使沒法中止操做。
    • 這是個人刪除操做。註釋後,是更改之後的操做。
    • 以前沒有加else,我並無想到會陷入無限循環。覺得當CurrentNode.number == element.number,跳出if,進行刪除操做。
      可是,並無跳出while循環,這時,我還犯了第二個錯誤,我覺得無限循環是整個鏈表的從頭至尾從頭至尾的不斷循環。其實否則,當CurrentNode.number != element.number時,CurrentNode就再也不改變了,也就是說CurrentNode一直不爲空,而且一直不等於element.number。
    • 在小組成員的幫助下,成功解決了問題,在此感謝段志軒和李馨雨同窗。這個問題,暴露出了兩個問題。第一,對於代碼的分析和理解能力還不足。沒有正確理解循環和跳出if和循環的條件。第二,對於代碼的細心程度不夠。沒有考慮到if的多種可能,沒有考慮到後續操做致使的後果。但願之後以此爲戒,多加改進。
  • 問題3:數組進行了添加操做,可是數組卻沒有變化。添加失敗?
    性能優化

  • 問題3解決方案:
    • 在我進行了調試之後,發現Insert()方法後返回值是沒有錯誤的。也就是說,對於Insert()方法是沒有任何錯誤的。
    • 那麼錯誤,就頗有可能發生在測試類的代碼編寫當中。問題中的截圖分別是個人第一次錯誤代碼,和第一次更改的代碼。然而,雖然,第一次更改解決了插入而且輸出新數組的操做,可是沒法再對新數組進行操做。因此,最後只能,建立另外一個新的數組來存儲排序後的數組。

其餘(感悟、思考等)

  • 線性結構的應用,在上學期已經接觸過,而且實現過部分。這是數據結構的基礎。不少時候,咱們並不能熟練的掌握,好比說讓你直接說出某種排序的原理和關鍵代碼。不少仍是須要咱們不斷積累和沉澱。
  • 慎終如始,則無敗事。

參考資料

相關文章
相關標籤/搜索