JavaRush /จาวาบล็อก /Random-TH /อัลกอริธึม Grocking หรือการแนะนำอัลกอริธึมอย่างไม่ลำบาก
Viacheslav
ระดับ

อัลกอริธึม Grocking หรือการแนะนำอัลกอริธึมอย่างไม่ลำบาก

เผยแพร่ในกลุ่ม
บทวิจารณ์หนังสือ "Grocking Algorithms" ความเห็นส่วนตัวเพียงเล็กน้อย ตัวอย่างบางส่วน ฉันหวังว่าบทวิจารณ์นี้จะช่วยให้คุณเข้าใจว่าคุณต้องการอ่านหนังสือเล่มนี้หรือไม่หรือจะไม่วางบนชั้นวางของคุณหรือไม่ คำเตือน: ข้อความจำนวนมาก)

“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด

การแนะนำ

ตำแหน่งงานว่างระดับจูเนียร์เกือบทุกตำแหน่งมีข้อกำหนดเช่น "ความรู้เกี่ยวกับโครงสร้างข้อมูลและอัลกอริธึม" สำหรับผู้ที่มีการศึกษาเฉพาะทางก็มีอัลกอริธึมรวมอยู่ในหลักสูตรทั่วไปแล้วไม่น่าจะมีปัญหาอะไร แต่จะเกิดอะไรขึ้นถ้าการพัฒนาถูกนำมาจากสเตปป์อื่น? สิ่งที่เหลืออยู่คือการเรียนรู้ด้วยตัวเอง มีคำตอบสำหรับคำถามที่ว่า “ใครจะตำหนิ” แต่สำหรับคำถาม “จะทำอย่างไร” ก็ต้องค้นหาคำตอบ มาดูในหนังสือกัน และฉันอยากจะบอกคุณเกี่ยวกับเรื่องหนึ่ง
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 1

อัลกอริธึม Grok

ในบรรดาผลงานทั้งหมด ฉันเจอหนังสือชื่อ "Grocking Algorithms" คุณสามารถหาข้อมูลเพิ่มเติมได้ที่นี่: “ หนังสือ “Growing Algorithms คู่มือพร้อมภาพประกอบสำหรับโปรแกรมเมอร์และผู้ที่มีความอยากรู้อยากเห็น ” ฉันสังเกตเห็นหนังสือเล่มนี้เมื่อนานมาแล้ว แต่สำหรับโอโซนมีราคา 680 รูเบิล แพงหรือถูก - ทุกคนตัดสินใจด้วยตัวเอง ฉันกำลังซื้อหนังสือเล่มที่สองบน Avito ในราคาเพียงครึ่งเดียวแล้ว ดังนั้นฉันจึงพบมันในเซนต์ปีเตอร์สเบิร์ก ซื้อมัน และไปคร่ำครวญ ซึ่งเป็นสิ่งที่ฉันตัดสินใจแบ่งปันกับคุณ ใช่ ไม่มีโค้ด Java ในหนังสือ แต่มี... โค้ดอื่น แต่จะเพิ่มเติมในภายหลัง

รู้เบื้องต้นเกี่ยวกับอัลกอริทึม (การเรียงลำดับการเลือก)

ดังนั้น ในรูปแบบการบรรยายที่ง่ายดาย เราจึงมาถึงลำดับแรกในการแสดงของเรา นี่คือการเรียงลำดับการเลือก สาระสำคัญของมันคือเราผ่านองค์ประกอบจากซ้ายไปขวา (จากองค์ประกอบ 0 ไปสุดท้าย) และมองหาองค์ประกอบที่ใหญ่ที่สุดในบรรดาองค์ประกอบที่เหลือ หากเราพบมัน เราก็จะสลับองค์ประกอบที่เราเป็นอยู่ตอนนี้และองค์ประกอบที่ใหญ่ที่สุด วิธีที่ง่ายที่สุดในการคิดอาร์เรย์เป็นอันดับแรกคือ: [5, 3, 6, 2, 10] หยิบกระดาษแผ่นหนึ่ง ปากกา (วิธีที่ง่ายที่สุดและถูกที่สุด) แล้วลองจินตนาการว่าเรามีเส้นขอบด้านซ้าย (ซ้าย) ดัชนีปัจจุบัน (หรือเส้นขอบขวา) อย่างไร มีดัชนีขององค์ประกอบขั้นต่ำ และเราทำงานร่วมกับมันอย่างไร ตัวอย่างเช่น:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 2
อัลกอริทึมมักอธิบายไว้ในรหัสเทียม เช่น ในวิกิพีเดีย ของเราไม่ใช่รหัสเทียม แต่จะเพิ่มเติมในภายหลัง ตอนนี้เรามาดูกัน:

def selectionSort(array):
    for left in range(0, len(array)):
        minIndex = left
        for right in range (left+1, len(array)):
            if array[right] < array[minIndex]:
                minIndex = right
        if minIndex != left:
            temp = array[left]
            array[left] = array[minIndex]
            array[minIndex] = temp
    return array

print(selectionSort([5, 3, 6, 2, 10]))
ตอนนี้ขอนำเสนอในรูปแบบของโค้ด Java:
public static void selectionSort(int[] array) {
        for (int left = 0; left < array.length; left++) {
            int minIndex = left;
            for (int right = left+1; right < array.length; right++) {
                if (array[right] < array[minIndex]) {
                    minIndex = right;
                }
            }
            if (minIndex != left) {
                int temp = array[left];
                array[left] = array[minIndex];
                array[minIndex] = temp;
            }
        }
}
อย่างที่คุณเห็นรหัสเกือบจะเหมือนกัน รหัสแรกเป็นตัวอย่างจากหนังสือ อย่างที่สองคือการดำเนินการฟรีของฉันในโค้ด Java

การเรียกซ้ำ

ต่อไปเราจะบอกว่ามีสิ่งที่เรียกว่าการเรียกซ้ำ ก่อนอื่นมีปัญหากับเกษตรกรที่มีนาขนาด AxB จำเป็นต้องแบ่งฟิลด์นี้ออกเป็น "กำลังสอง" เท่า ๆ กัน และหลังจากนั้นก็กล่าวถึง Euclid Algorithm สิ่งที่ฉันไม่ชอบก็คือพวกเขาไม่ได้พยายามเขียนโค้ดของมัน แต่อัลกอริทึม Euclid นั้นง่ายและมีประสิทธิภาพ:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริธึมที่ไม่เจ็บปวด - 3
พูดตามตรง ฉันพลาดรายละเอียดบางอย่างในหนังสือไป อย่างเช่นในวิดีโอนี้: “ สารสนเทศ ทฤษฎีอัลกอริธึม อัลกอริธึมของ Euclid ” ตัวอย่างเช่น ถ้า a น้อยกว่า b ดังนั้นในระหว่างการรันครั้งแรก b และ a จะเปลี่ยนตำแหน่ง และครั้งที่สองที่ใหญ่กว่าจะถูกหารด้วยอันที่เล็กกว่า ดังนั้นลำดับของการโต้แย้งจึงไม่สำคัญ ตามปกติ ก่อนอื่นเราสามารถ "สัมผัส" อัลกอริธึมบนกระดาษได้:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 4
ตอนนี้เรามาดูโค้ดกัน:

def euclidean(a, b):
    if a == 0 : return b
    if b == 0 : return a
    return euclidean (b, a % b)
มาเขียนโค้ดเดียวกันใน Java กัน หากต้องการเราสามารถใช้คอมไพเลอร์ออนไลน์ได้ :
public static int euclid(int a, int b) {
        if (a == 0) return b;
        if (b == 0) return a;
        return euclid(b, a%b);
}
แฟกทอเรียลถูกกล่าวถึงในตอนต้นของหนังสือด้วย แฟกทอเรียลของตัวเลข n (n!) คือผลคูณของตัวเลขตั้งแต่ 1 ถึง n ทำไมทำเช่นนี้? มีแอปพลิเคชั่นที่ใช้งานได้จริงอย่างหนึ่งที่นี่ หากเรามีสิ่งของ n ชิ้น (เช่น n เมือง) เราก็สามารถสร้างสิ่งของเหล่านั้นขึ้นมาได้! การรวมกัน คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการเรียกซ้ำได้ที่นี่: “ การเรียกซ้ำ งานการฝึกอบรม ” การเปรียบเทียบวิธีการวนซ้ำและแบบเรียกซ้ำ: " การเรียกซ้ำ "

จัดเรียงอย่างรวดเร็ว

การเรียงลำดับอย่างรวดเร็วเป็นอัลกอริทึมที่ค่อนข้างน่าสนใจ หนังสือไม่ได้สนใจเขามากนัก นอกจากนี้ รหัสจะได้รับเฉพาะในกรณีที่เลวร้ายที่สุด เมื่อเลือกองค์ประกอบแรก อย่างไรก็ตาม บางทีสำหรับคนรู้จักครั้งแรกตัวอย่างนี้อาจจดจำได้ง่ายกว่า และเป็นการดีกว่าที่จะเขียน Quicksort ที่ไม่ดีมากกว่าที่จะไม่เขียนเลย นี่คือตัวอย่างจากหนังสือ:

def quicksort(array):
    if len(array) < 2:
        return array
    else:
        pivot = array[0]
        less = [i for i in array[1:] if i <= pivot]
        greater = [i for i in array[1:] if i > pivot]
    return quicksort(less) + [pivot] + quicksort(greater)
ทุกอย่างที่นี่เรียบง่ายมาก หากเรามีอาร์เรย์เป็น 0 หรือ 1 องค์ประกอบ ก็ไม่จำเป็นต้องเรียงลำดับ หากมากกว่านั้น เราจะถือว่าองค์ประกอบแรกของอาร์เรย์และพิจารณาว่าเป็น "องค์ประกอบสาระสำคัญ" เราสร้างอาร์เรย์ใหม่ 2 อาร์เรย์ - อาร์เรย์หนึ่งมีองค์ประกอบที่มีขนาดใหญ่กว่าเดือย และอาร์เรย์ที่สองมีองค์ประกอบที่เล็กกว่า และเราทำซ้ำแบบวนซ้ำ ไม่ใช่ตัวเลือกที่ดีที่สุด แต่จำได้ดีกว่าอีกครั้ง ลองใช้อัลกอริทึมนี้ใน Java แต่ให้ถูกต้องมากขึ้น เนื้อหาจากการทบทวน “ วิทยาการคอมพิวเตอร์ใน JavaScript: Quicksort ” จะช่วยเราในเรื่องนี้. และก่อนที่จะเขียนโค้ด มาวาดอีกครั้งเพื่อ "สัมผัส" อัลกอริธึม: ก่อนอื่น มาวาดอีกครั้งบนกระดาษเพื่อทำความเข้าใจอัลกอริธึม:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริธึมที่ไม่เจ็บปวด - 5
สำหรับฉันดูเหมือนว่าช่วงเวลาที่อันตรายที่สุดช่วงหนึ่งคือการแก้ปัญหาโดยสิ้นเชิง ดังนั้นเราจะดำเนินการตามขั้นตอนเล็กๆ หลายประการ:
  • เราจำเป็นต้องสามารถสลับองค์ประกอบในอาร์เรย์ได้:

    private static void swap(int[] array, int firstIndex, int secondIndex) {
            int temp = array[firstIndex];
            array[firstIndex] = array[secondIndex];
            array[secondIndex] = temp;
    }

  • เราต้องการวิธีการที่แบ่งอาร์เรย์ตามช่วงเวลาที่กำหนดออกเป็น 3 ส่วน


    private static int partition(int[] array, int left, int right) {
            int pivot = array[(right + left) / 2];
            while (left <= right) {
                while (array[left] < pivot) {
                    left++;
                }
                while (array[right] > pivot) {
                    right--;
                }
                if (left <= right) {
                    swap(array, left, right);
                    left++;
                    right--;
                }
            }
            return left;
    }

    รายละเอียดตามลิงค์ด้านบนครับ กล่าวโดยย่อคือ เราเลื่อนเคอร์เซอร์ซ้ายจนกว่าองค์ประกอบจะน้อยกว่าเดือย ในทำนองเดียวกัน ให้เลื่อนเคอร์เซอร์ขวาจากปลายอีกด้านหนึ่ง และเราจะทำการสลับหากเคอร์เซอร์ไม่ตรงกัน เราดำเนินต่อไปจนกระทั่งเคอร์เซอร์มาบรรจบกัน เราส่งคืนดัชนีที่แบ่งการประมวลผลเพิ่มเติมออกเป็น 2 ส่วน

  • มีการแยก เราต้องการการเรียงลำดับเอง:

    public static void quickSort(int[] array, int left, int right) {
            int index = 0;
            if (array.length > 1) {
                index = partition(array, left, right);
                if (left < index - 1) {
                    quickSort(array, left, index - 1);
                }
                if (index < right) {
                    quickSort(array, index, right);
                }
            }
    }

    นั่นคือถ้าอาร์เรย์ประกอบด้วยองค์ประกอบอย่างน้อยสององค์ประกอบ ก็สามารถเรียงลำดับได้แล้ว ขั้นแรก เราแบ่งอาร์เรย์ทั้งหมดออกเป็นสองส่วน องค์ประกอบที่เล็กกว่าเดือยและองค์ประกอบที่ใหญ่กว่า จากนั้นเราจะดำเนินการที่คล้ายกันสำหรับแต่ละส่วนที่เป็นผล

    และสำหรับการทดสอบ:

    public static void main(String []args){
            int[] array = {8,9,3,7,6,7,1};
            quickSort(array, 0, array.length-1);
            System.out.println(Arrays.toString(array));
    }
หนังสือระบุว่าอัลกอริทึมนี้เป็นของอัลกอริธึมที่เรียกว่า "Divide and Conquer" เมื่อชุดข้อมูลที่ประมวลผลถูกแบ่งครึ่งในแต่ละครั้ง ความซับซ้อนของอัลกอริทึม: O(nLogn)
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 6
สิ่งที่ไม่ดี (นั่นคือสิ่งที่ฉันไม่ชอบ) ก็คือหนังสือเล่มนี้กล่าวถึงการเรียงลำดับแบบผสานในการส่งผ่าน แต่ไม่มีตัวอย่างหรือโค้ดใดๆ ดูรายละเอียดเพิ่มเติมได้ที่นี่: " สารสนเทศ อัลกอริธึมการค้นหาและการเรียงลำดับ: การเรียงลำดับแบบผสาน " ดังนั้นเพื่อความสม่ำเสมอเรามาทำเองกันดีกว่า แน่นอนว่าอัลกอริทึมนั้นเรียบง่ายและตรงไปตรงมาโดยเนื้อแท้:
public static void mergeSort(int[] source, int left, int right) {
    if ((right - left) > 1) {
        int middle = (right + left) / 2;
        mergeSort(source, left, middle);
        mergeSort(source, middle + 1, right);
    }
    merge(source, left, right);
}
เรากำหนดตรงกลางและแบ่งอาร์เรย์ออกเป็นสองส่วน สำหรับแต่ละครึ่งเราทำแบบเดียวกันไปเรื่อยๆ เงื่อนไขการหยุดหรือกรณีฐาน - เราต้องมีมากกว่าหนึ่งองค์ประกอบ เนื่องจากเราไม่สามารถแบ่งองค์ประกอบหนึ่งออกเป็นสองได้ ตอนนี้เราจำเป็นต้องดำเนินการควบรวมกิจการ นั่นคือ ผสาน:
public static void merge(int[] array, int from, int to) {
    int middle = ((from + to) / 2) + 1;
    int left = from;
    int right = middle;
    int cursor = 0;

    int[] tmp = new int[to - from + 1];
    while (left < middle || right <= to) {
        if (left >= middle) {
            tmp[cursor] = array[right];
            System.out.println("Остаток справа: " + array[right]);
            right++;
        } else if (right > to) {
            tmp[cursor] = array[left];
            System.out.println("Остаток слева: " + array[left]);
            left++;
        } else if (array[left] <= array[right]) {
            tmp[cursor] = array[left];
            System.out.println("Слева меньше: " + array[left]);
            left++;
        } else if (array[right] < array[left]) {
            tmp[cursor] = array[right];
            System.out.println("Справа меньше: " + array[right]);
            right++;
        }
        cursor++;
    }
    System.arraycopy(tmp, 0, array, from, tmp.length);
}
ไม่มีอะไรจะแสดงความคิดเห็นมากนักที่นี่ จากชื่อของตัวแปรprintlnทุกอย่างชัดเจน เพื่อตรวจสอบ:
int array[] = {1, 7, 3, 6, 7, 9, 8, 4};
mergeSort(array, 0, array.length - 1);
System.out.println(Arrays.toString(array));

ตารางแฮช

หนังสือเล่มนี้ยังสัมผัสกับตารางแฮชอีกด้วย คุณไม่จำเป็นต้องดำเนินการด้วยตนเอง และสาระสำคัญของตารางแฮชนั้นค่อนข้างง่าย ท้ายที่สุดแล้ว Java ยังมีการใช้งานตารางแฮช java.util.HashTable ถ้าเราดูที่อุปกรณ์ HashTable เราจะเห็นว่าอาร์เรย์รายการอาศัยอยู่ภายใน รายการคือเรกคอร์ดที่เป็นการรวมกันของคีย์ – ค่า HashTable มี defaultCapacity - นั่นคือขนาดเริ่มต้น และ loadFactor – ปัจจัยโหลด ค่าเริ่มต้นคือ 0.75 ตัวเลขนี้จะบอกคุณว่าโหลดของอาร์เรย์ (จำนวนองค์ประกอบ/จำนวนทั้งหมด) ใดที่ต้องเพิ่มขนาด ใน Java มันเพิ่มขึ้น 2 เท่า หนังสืออธิบายว่าตารางแฮชเรียกว่าตารางแฮช เนื่องจากขึ้นอยู่กับฟังก์ชันแฮช ซึ่งก็คือเซลล์อาร์เรย์ (ตะกร้า) ซึ่งไฟล์Entry. คุณยังสามารถอ่านเพิ่มเติมได้ที่นี่: โครงสร้างข้อมูลในรูปภาพ HashMapและLinkedHashMap _ คุณยังสามารถอ่านได้ในหนังสือ ตัวอย่างที่นี่: " พื้นฐาน HashTable "

กราฟ, การค้นหาแบบกว้างก่อน (การค้นหาเส้นทางที่สั้นที่สุด)

อาจเป็นหนึ่งในหัวข้อที่น่าสนใจที่สุดคือกราฟ พูดตามตรงแล้วหนังสือเล่มนี้ให้ความสำคัญกับพวกเขาเป็นอย่างมาก บางทีนั่นอาจเป็นเหตุผลว่าทำไมจึงคุ้มค่าที่จะอ่านหนังสือเล่มนี้ แม้ว่าบางทีอาจจะระบุไว้ชัดเจนกว่านี้อีกหน่อย)) แต่เรามีอินเทอร์เน็ตและนอกเหนือจากหนังสือแล้ว คุณสามารถดูเพลย์ลิสต์เกี่ยวกับทฤษฎีนี้สำหรับ "ผู้ที่ได้ยินเกี่ยวกับกราฟเป็นครั้งแรก " ” ในตอนต้นของหนังสือ อัลกอริธึมการค้นหาแบบกว้างต้องมาก่อนbreadth-first-searchหรือที่เรียกว่า BFS ได้ถูกมอบให้ไว้ หนังสือเล่มนี้ประกอบด้วยกราฟต่อไปนี้:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 7
หนังสือระบุว่าคิวจะช่วยเรา ยิ่งไปกว่านั้น ทำให้เราสามารถเพิ่มองค์ประกอบที่ส่วนท้ายและประมวลผลคิวตั้งแต่ต้นได้ คิวดังกล่าวเรียกว่าคิวสองทางหรือ Deque ในภาษาอังกฤษ หนังสือเล่มนี้แนะนำให้ใช้โครงสร้างข้อมูล - ตารางแฮช เพื่อเชื่อมโยงชื่อและเพื่อนบ้าน ด้วยจุดยอดที่มีตัวเลข คุณสามารถใช้อาร์เรย์ได้ การจัดเก็บจุดยอดนี้เรียกว่า "รายการจุดยอดที่อยู่ติดกัน" ซึ่งไม่ได้กล่าวถึงในหนังสือ นี่คือลบสำหรับพวกเขา ลองใช้สิ่งนี้ใน Java:
private Map<String, String[]> getGraph() {
    Map<String, String[]> map = new HashMap<>();
    map.put("you", new String[]{"alice", "bob", "claire"});
    map.put("bob", new String[]{"anuj", "peggy"});
    map.put("alice", new String[]{"peggy"});
    map.put("claire", new String[]{"thom", "jonny"});
    map.put("annuj", null);
    map.put("peggy", null);
    map.put("thom", null);
    map.put("johny", null);
    return map;
}
ตอนนี้การค้นหานั้นสร้างขึ้นจากข้อมูลนี้:
private String search() {
    Map<String, String[]> graph = getGraph();
    Set<String> searched = new HashSet<>();
    Deque<String> searchQue = new ArrayDeque<>();
    searchQue.add("you");
    while (!searchQue.isEmpty()) {
        String person = searchQue.pollFirst();
        System.out.println(person);
        if (personIsSeller(person)) {
            return person;
        } else {
            String[] friends = graph.get(person);
            if (friends == null) continue;
            for (String friend : friends) {
                if (friend != null && !searched.contains(friend)) {
                    searchQue.addLast(friend);
                }
            }
        }
    }
    return null;
}
อย่างที่คุณเห็นไม่มีอะไรซับซ้อน หากเปรียบเทียบกับโค้ดจากหนังสือก็เกือบจะเหมือนกัน

กราฟ อัลกอริธึมของไดจ์คสตรา

เมื่อมีความเข้าใจ BFS ไม่มากก็น้อย ผู้เขียนหนังสือเล่มนี้ขอเชิญชวนให้เราเข้าใจอัลกอริทึม Daysktra และกราฟถ่วงน้ำหนัก กราฟต่อไปนี้ถูกเสนอสำหรับการแก้ปัญหา:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 8
ขั้นแรก เราต้องเข้าใจวิธีการแสดงกราฟของเรา เราสามารถแสดงมันเป็นเมทริกซ์ได้ บทความเกี่ยวกับHabréจะช่วยเราได้ที่นี่: อัลกอริทึมของ Dijkstra การค้นหา เส้นทางที่เหมาะสมที่สุดบนกราฟ ลองใช้เมทริกซ์คำคุณศัพท์:
public Integer[][] getGraphMatrix(int size) {
    Integer[][] matrix = new Integer[size][size];
    matrix[0][1] = 6;
    matrix[0][2] = 2;
    matrix[2][1] = 3;
    matrix[1][3] = 1;
    matrix[2][3] = 5;
    return matrix;
}
และตอนนี้ตรรกะนั้นเอง:
@Test
public void dijkstra() {
    Integer[][] graph = getGraphMatrix();           // Данные графа
    Integer[] costs = new Integer[graph.length];    // Стоимость перехода
    Integer[] parents = new Integer[graph.length];  // Родительский узел
    BitSet visited = new BitSet(graph.length);      // "Ферма" маркеров посещённости

    Integer w = 0;
    do {
        System.out.println("-> Рассматриваем вершину: " + w);
        Integer min = null;
        for (int i = 0; i < graph.length; i++) {    // Обрабатываем каждую дугу
            if (graph[w][i] == null) continue;      // Дуги нет - идём дальше
            if (min == null || (!visited.get(i) && graph[w][min] > graph[w][i])) {
                min = i;
            }
            if (costs[i] == null || costs[i] > costs[w] + graph[w][i]) {
                System.out.print("Меням вес с " + costs[i]);
                costs[i] = (costs[w] != null ? costs[w] : 0) + graph[w][i];
                System.out.println(" на " + costs[i] + " для вершины " + i);
                parents[i] = w;
            }
        }
        System.out.println("Вершина с минимальным весом: " + min);
        visited.set(w);
        w = min;
    } while (w != null);

    System.out.println(Arrays.toString(costs));
    printPath(parents, 3);
}

public void printPath(Integer[] parents, int target) {
    Integer parent = target;
    do {
        System.out.print(parent + " <- ");
        parent = parents[parent];
    } while (parent != null);
}
หนังสือแบ่งเนื้อหาออกเป็นขั้นๆ หากคุณเพิ่มบทความเกี่ยวกับHabréบนอินเทอร์เน็ต + ดูโค้ดคุณก็จำได้ ฉันพบว่าการวิเคราะห์ทีละขั้นตอนค่อนข้างยุ่งเหยิง แต่สำหรับธรรมชาติทีละขั้นตอนแล้วมันก็ถือเป็นข้อดี โดยรวมโอเคครับแต่น่าจะดีกว่านี้)

อัลกอริธึมโลภ

หัวข้อถัดไปเกี่ยวข้องกับ "อัลกอริธึมโลภ" ส่วนนี้น่าสนใจเนื่องจากใช้ชุด (java.util.Set) ในที่สุดเราก็สามารถเห็นได้ว่าทำไมมันถึงจำเป็น เราใช้รายการสถานะเป็นอินพุต:
Set<String> statesNeeded = new HashSet();
statesNeeded.addAll(Arrays.asList("mt", "wa", "or", "id", "nv", "ut", "ca", "az" ));
และยังมีรายชื่อสถานีวิทยุที่ครอบคลุมบางรัฐเหล่านี้:
Map<String, Set<String>> stations = new HashMap<>();
stations.put("kone", new HashSet(Arrays.asList("id", "nv", "ut")));
stations.put("ktwo", new HashSet(Arrays.asList("wa", "id", "mt")));
stations.put("kthree", new HashSet(Arrays.asList("or", "nv", "ca")));
stations.put("kfour", new HashSet(Arrays.asList("nv", "ut")));
stations.put("kfive", new HashSet(Arrays.asList("ca", "az")));
หนังสือเล่มนี้ชี้ให้เห็นและอธิบายอัลกอริทึมต่อไป:
Set<String> finalStations = new HashSet();
while (!statesNeeded.isEmpty()) {
    String bestStation = null;
    Set<String> statesCovered = new HashSet();
    for (String station: stations.keySet()) {
        Set covered = new HashSet(statesNeeded);
        covered.retainAll(stations.get(station));
        if (covered.size() > statesCovered.size()) {
           bestStation = station;
           statesCovered = covered;
        }
    }
    statesNeeded.removeAll(statesCovered);
    finalStations.add(bestStation);
}
System.out.println(finalStations);

การเขียนโปรแกรมแบบไดนามิก

หนังสือเล่มนี้ยังอธิบายถึงปัญหาที่ใช้แนวทางที่เรียกว่า "การเขียนโปรแกรมแบบไดนามิก" งานที่ได้รับ:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 9
เรามีกระเป๋าขนาด 4 ปอนด์ คุณต้องค้นหาสินค้าที่ทำกำไรได้มากที่สุดตามน้ำหนักที่กำหนด ขั้นแรก เรามาสร้างรายการกันก่อน:
List<Thing> things = new ArrayList<>();
things.add(new Thing("guitar", 1, 1500));
things.add(new Thing("tape recorder", 4, 3000));
things.add(new Thing("notebook", 3, 2000));
ตอนนี้อัลกอริทึมเอง:
int bagSize = 4;
int cell[][] = new int[things.size()][bagSize];
// Заполняем первую строку без условий
for (int i = 0; i < bagSize; i++) {
    cell[0][i] = things.get(0).cost;
}
// Заполняем оставшиеся
for (int i = 1; i < cell.length; i++) {
    for (int j = 0; j < cell[i].length; j++) {
        // Если вещь не влезает - берём прошлый максимум
        if (things.get(i).weight > j+1) {
            cell[i][j] = cell[i - 1][j];
        } else {
            // Иначе текущая стоимость + предыдущий максимум оставшегося размера
            cell[i][j] = things.get(i).cost;
            if (j + 1 - things.get(i).weight > 0) {
                cell[i][j] += cell[i-1][j + 1 - things.get(i).weight];
            }
        }
    }
}
System.out.println(Arrays.deepToString(cell));
นอกจากนี้ยังมีงานที่น่าสนใจในการค้นหาคำที่คล้ายกันมากที่สุด น่าสนใจไม่ใช่เหรอ? รายละเอียดเพิ่มเติมที่นี่: LongestCommonSubsequence.java

ค้นหาเพื่อนบ้านที่ใกล้ที่สุด

หนังสือเล่มนี้ยังพูดถึงอัลกอริธึมเพื่อนบ้านที่ใกล้ที่สุดอย่างชัดเจน:
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริทึมที่ไม่เจ็บปวด - 10
และให้สูตรการคำนวณดังนี้
“อัลกอริธึม Grocking” หรือความรู้เบื้องต้นเกี่ยวกับอัลกอริธึมที่ไม่เจ็บปวด - 11

บรรทัดล่าง

หนังสือเล่มนี้จบลงด้วยส่วน "อะไรต่อไป?" ที่น่าสนใจ ซึ่งให้ภาพรวมโดยย่อของอัลกอริทึมที่น่าสนใจ ต่อไปนี้เป็นคำอธิบายโดยย่อเกี่ยวกับความหมายของต้นไม้และอัลกอริทึมอื่นๆ โดยรวมแล้วฉันชอบหนังสือเล่มนี้ ไม่ควรถือเป็นข้อมูลที่ครอบคลุมบางประเภทอย่างจริงจัง คุณจะต้องค้นหาและเข้าใจด้วยตัวเอง แต่เป็นข้อมูลเบื้องต้นที่น่าสนใจและให้แนวคิดเบื้องต้นก็ค่อนข้างดี ใช่แล้ว รหัสในหนังสือเขียนด้วยภาษา Python ตัวอย่างข้างต้นทั้งหมดสามารถรวบรวมได้) ฉันหวังว่าการทบทวนนี้จะช่วยให้คุณเข้าใจว่าหนังสือเล่มนี้มีอะไรบ้างและคุ้มค่าที่จะซื้อหรือไม่

นอกจากนี้

คุณยังสามารถดูแหล่งข้อมูลต่อไปนี้ในหัวข้อนี้ได้:
  1. EdX - การเขียนโปรแกรม Java เบื้องต้น: โครงสร้างข้อมูลพื้นฐานและอัลกอริทึม
  2. LinkedIn - ข้อมูลเบื้องต้นเกี่ยวกับโครงสร้างข้อมูลและอัลกอริทึมใน Java (ชำระเงิน)
  3. 27 ไซต์พร้อมปริศนาเพื่อฝึกฝนทักษะการเขียนโปรแกรมของคุณ
  4. Java CodingBat
  5. งานสำหรับโปรแกรมเมอร์ คำตอบสำหรับงานที่มีความซับซ้อนต่างกัน
#เวียเชสลาฟ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION