1. Trang chủ
  2. » Công Nghệ Thông Tin

Bài giảng cấu trúc dữ liệu và giải thuật

194 5 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Cấu Trúc Dữ Liệu Và Giải Thuật
Tác giả ThS. Hoàng Thế Phương
Trường học Đại Học Công Nghệ Giao Thông Vận Tải
Chuyên ngành Cấu Trúc Dữ Liệu Và Giải Thuật
Thể loại Lưu Hành Nội Bộ
Năm xuất bản 2019
Thành phố Hà Nội
Định dạng
Số trang 194
Dung lượng 2,27 MB

Cấu trúc

  • Chương 1: Các khái niệm cơ bản (5)
    • 1.1. Các thành phần cơ bản của ngôn ngữ lập trình C (5)
      • 1.1.1. Tập ký tự (5)
      • 1.1.2. Từ khóa (5)
      • 1.1.3. Tên (6)
      • 1.1.4. Kiểu dữ liệu (7)
      • 1.1.5. Hằng (10)
      • 1.1.6. Biến (16)
    • 1.2. Các khái niệm cơ bản về giải thuật (17)
      • 1.2.1. Khái niệm về giải thuật và cấu trúc dữ liệu (17)
      • 1.2.2. Cấu trúc dữ liệu và các vấn đề liên quan (18)
      • 1.2.3. Diễn đạt giải thuật (20)
    • 1.3. Phân tích và thiết kế giải thuật (25)
      • 1.3.1. Từ bài toán đến chương trình (25)
      • 1.3.2. Phân tích, thiết kế giải thuật (31)
  • Chương 2. Các thành phần cơ bản và cấu trúc điều khiển chương trình (37)
    • 2.1. Các lệnh vào ra dữ liệu (37)
      • 2.1.1. Các hàm vào ra chuẩn (37)
      • 2.1.2. Đưa kết quả lên màn hình (39)
      • 2.1.3. Vào dữ liệu từ bàn phím (44)
    • 2.2. Biểu thức (49)
      • 2.2.1. Khái niệm (49)
      • 2.2.2. Lệnh gán và biểu thức (49)
      • 2.2.3. Các phép toán (50)
      • 2.2.4. Chuyển đổi kiểu giá trị (56)
    • 2.3. Cấu trúc cơ bản của chương trình (59)
      • 2.3.1. Lời chú thích (59)
      • 2.3.2. Lệnh và khối lệnh (60)
      • 2.3.4. Cấu trúc cơ bản của chương trình (65)
      • 2.3.5. Quy tắc khi viết chương trình (67)
    • 2.4. Cấu trúc điều kiện if (68)
      • 2.4.1. Lệnh if-else (68)
      • 2.4.2. Lệnh else-if (71)
    • 2.5. Cấu trúc rẽ nhánh switch…case (73)
    • 2.6. Cấu trúc lặp for (77)
    • 2.7. Cấu trúc lặp while (82)
      • 2.7.1. Cấu trúc while (82)
      • 2.7.2. Cấu trúc do-while (85)
    • 2.8. Câu lệnh nhảy (87)
      • 2.8.1. Lệnh nhảy không điều kiện - toán tử goto (87)
      • 2.8.2. Câu lệnh break (89)
      • 2.8.3. Câu lệnh continue (90)
  • Chương 3. Hàm và con trỏ (93)
    • 3.1. Hàm (93)
      • 3.1.1. Khái niệm, khai báo hàm (93)
      • 3.1.2. Cách tổ chức hàm (93)
      • 3.1.3. Cách truyền tham số khi gọi hàm (97)
    • 3.2. Con trỏ (106)
      • 3.2.1. Con trỏ và địa chỉ (106)
      • 3.2.2. Con trỏ và mảng một chiều (109)
      • 3.2.3. Con trỏ và mảng nhiều chiều (115)
      • 3.2.4. Các phép toán trên con trỏ (118)
      • 3.2.5. Mảng con trỏ (121)
      • 3.2.6. Con trỏ tới hàm (124)
  • Chương 4. Cấu trúc dữ liệu (129)
    • 4.1. Mảng và danh sách (129)
      • 4.1.1. Các khái niệm (129)
      • 4.1.2. Cấu trúc lưu trữ mảng (131)
      • 4.1.3. Danh sách tuyến tính (133)
    • 4.2. Ngăn xếp (140)
      • 4.2.1. Định nghĩa ngăn xếp (140)
      • 4.2.2. Lưu trữ ngăn xếp (140)
      • 4.2.3. Ứng dụng của ngăn xếp (145)
    • 4.3. Hàng đợi (147)
      • 4.3.1. Định nghĩa hàng đợi (147)
      • 4.3.2. Lưu trữ hàng đợi (148)
    • 4.4. Cây (153)
      • 4.4.1. Các khái niệm (153)
      • 4.4.2. Cây nhị phân (154)
      • 4.4.3. Cây tổng quát (158)
    • 4.5. Đồ thị (163)
      • 4.5.1. Các khái niệm (163)
      • 4.5.2. Biểu diễn đồ thị (165)
      • 4.5.3. Phép duyệt một đồ thị (168)
      • 4.5.4. Áp dụng (172)
  • Chương 5. Giải thuật sắp xếp và tìm kiếm (174)
    • 5.1. Sắp xếp (174)
      • 5.1.1. Đặt vấn đề (174)
      • 5.1.2. Sắp xếp chọn trực tiếp (174)
      • 5.1.3. Sắp xếp chèn trực tiếp (177)
      • 5.1.4. Sắp xếp đổi chỗ trực tiếp (181)
      • 5.1.5. Sắp xếp trộn (185)
    • 5.2. Tìm kiếm (188)
      • 5.2.1. Bài toán tìm kiếm (188)
      • 5.2.2. Tìm kiếm tuần tự (189)
      • 5.2.3. Tìm kiếm nhị phân (192)

Nội dung

Các khái niệm cơ bản

Các thành phần cơ bản của ngôn ngữ lập trình C

Mọi ngôn ngữ lập trình đều được xây dựng từ một bộ ký tự nhất định, mà từ đó các ký tự được nhóm lại để tạo thành từ ngữ Những từ này liên kết với nhau theo quy tắc nhất định, hình thành nên các câu lệnh Một chương trình bao gồm nhiều câu lệnh và thể hiện một thuật toán nhằm giải quyết một bài toán cụ thể Ngôn ngữ C được xây dựng dựa trên bộ ký tự này.

10 chữ số : 0 1 2 9 Các ký hiệu toán học : + - * / = ( )

Ký tự gạch nối : _ Các ký tự khác : , : ; [ ] {} ! \ & % # $

Dấu cách (space) dùng để tách các từ Ví dụ chữ VIET NAM có 8 ký tự, còn VIETNAM chỉ có 7 ký tự

Khi viết chương trình, ta không được sử dụng bất kỳ ký tự nào khác ngoài các ký tự trên

Khi lập trình giải phương trình bậc hai ax² + bx + c = 0, cần tính biệt thức Delta (Δ) với công thức Δ = b² - 4ac Tuy nhiên, trong ngôn ngữ C, ký tự Δ không được phép sử dụng, do đó cần thay thế bằng ký hiệu khác.

Keywords are terms used to declare data types, write operators, and construct statements in programming The following table lists the keywords of TURBO C: asm, break, case, cdecl, char, const, continue, default, do, double, else, enum, extern, far, float, for, goto, huge, if, int, interrupt, long, near, pascal, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while The meanings and usage of each keyword will be discussed later, but it is important to note their significance in programming.

- Không được dùng các từ khoá để đặt tên cho các hằng, biến, mảng, hàm

- Từ khoá phải được viết bằng chữ thường, ví dụ : viết từ khoá khai báo kiểu nguyên là int chứ không phải là INT.

Tên là khái niệm quan trọng trong lập trình, được sử dụng để xác định các đại lượng khác nhau như tên hằng, tên biến, tên mảng, tên hàm, tên con trỏ, tên tệp, tên cấu trúc và tên nhãn.

Tên được đặt theo qui tắc sau :

Tên là chuỗi ký tự bao gồm chữ cái, số và gạch nối, với ký tự đầu tiên phải là chữ cái hoặc gạch nối Tên không được trùng với khóa và có độ dài tối đa mặc định là 32 ký tự, có thể điều chỉnh trong khoảng từ 1 đến 32.

32 nhờ chức năng : Option-Compiler-Source-Identifier length khi dùng TURBO C

Các tên đúng : a_1 delta x1 _step GAMA

3MN Ký tự đầu tiên là số m#2 Sử dụng ký tự # f(x) Sử dụng các dấu ( ) do Trùng với từ khoá te ta Sử dụng dấu trắng

Trong TURBO C, tên biến phân biệt chữ hoa và chữ thường, ví dụ AB khác với ab Thông thường, trong ngôn ngữ C, chữ hoa được sử dụng để đặt tên cho các hằng, trong khi chữ thường thường được dùng cho các đại lượng khác như biến, mảng, hàm và cấu trúc Tuy nhiên, quy tắc này không phải là bắt buộc.

Trong C sử dụng các các kiểu dữ liệu sau : a Kiểu ký tự (char) :

Một giá trị kiểu char chiếm 1 byte ( 8 bit ) và biểu diễn được một ký tự thông qua bảng mã ASCII Ví dụ :

Có hai kiểu dữ liệu char : kiểu signed char và unsigned char

Kiểu Phạm vi biểu diễn Số ký tự char (Signed char) -128 đến 127 256 1 byte unsigned char 0 đến 255 256 1 byte

Ví dụ sau minh hoạ sự khác nhau giữa hai kiểu dữ liệu trên : Xét đoạn chương trình sau : char ch1; unsigned char ch2;

Khi đó thực chất : ch1=-56;

Nhưng cả ch1 và ch2 đều biểu diễn cùng một ký tự có mã 200

Có thể chia 256 ký tự làm ba nhóm :

Nhóm 1: Nhóm các ký tự điều khiển có mã từ 0 đến 31 Chẳng hạn ký tự mã

Ký tự 13 được sử dụng để đưa con trỏ về đầu dòng, trong khi ký tự 10 giúp chuyển con trỏ xuống dòng tiếp theo trong cùng một cột Những ký tự này thường không hiển thị trên màn hình.

Nhóm 2 : Nhóm các ký tự văn bản có mã từ 32 đến 126 Các ký tự này có thể được đưa ra màn hình hoặc máy in

Nhóm 3 : Nhóm các ký tự đồ hoạ có mã số từ 127 đến 255 Các ký tự này có thể đưa ra màn hình nhưng không in ra được ( bằng các lệnh DOS ) b Kiểu nguyên :

Trong ngôn ngữ lập trình C, có ba kiểu số nguyên chính được sử dụng: kiểu số nguyên int, kiểu số nguyên dài long và kiểu số nguyên không dấu unsigned Kích cỡ và phạm vi biểu diễn của các kiểu số nguyên này được mô tả chi tiết trong bảng dưới đây.

Kiểu Phạm vi biểu diễn Kích thước int -32768 đến 32767 2 byte unsigned int 0 đến 65535 2 byte long -2147483648 đến 4 byte

Kiểu ký tự cũng có thể xem là một dạng của kiểu nguyên c Kiểu dấu phảy động :

Trong ngôn ngữ lập trình C, có ba loại dữ liệu dấu phẩy động được sử dụng, bao gồm float, double và long double Kích thước và phạm vi biểu diễn của các loại dữ liệu này được thể hiện rõ trong bảng dưới đây.

Kiểu Phạm vi biểu diễn Số chữ số Kích thước có nghĩa Float 3.4E-38 đến 3.4E+38 7 đến 8 4 byte

1.7E+308 long double 3.4E-4932 đến 17 đến 18 10 byte

Máy tính có khả năng lưu trữ các số kiểu float với giá trị tuyệt đối từ 3.4E-38 đến 3.4E+38, trong khi các số có giá trị nhỏ hơn 3.4E-38 được coi là bằng 0 Phạm vi biểu diễn của số kiểu double cũng tương tự như vậy.

Hằng là các đại lượng mà giá trị của nó không thay đổi trong quá trình tính toán a Tên hằng :

Nguyên tắc đặt tên hằng ta đã xem xét trong mục 1.3 Để đặt tên một hằng, ta dùng dòng lệnh sau :

#define tên hằng giá trị

Trong chương trình, tất cả các tên MAX được thay thế bằng 1000, do đó MAX thường được gọi là tên hằng, biểu thị cho giá trị 1000.

#define pi 3.141593 Đặt tên cho một hằng float là pi có giá trị là 3.141593 b Các loại hằng :

Hằng int là số nguyên có giá trị trong khoảng từ -32768 đến 32767

#define number1 -50 Định nghiã hằng int number1 có giá trị là -50

#define sodem 2732 Định nghiã hằng int sodem có giá trị là 2732

Cần phân biệt hai hằng 5056 và 5056.0 : ở đây 5056 là số nguyên còn

Hằng long là số nguyên có giá trị trong khoảng từ -2147483648 đến

Hằng long được viết theo cách :

1234L hoặc 1234l ( thêm L hoặc l vào đuôi ) Một số nguyên vượt ra ngoài miền xác định của int cũng được xem là long

#define sl 8865056L Định nghiã hằng long sl có giá trị là 8865056

#define sl 8865056 Định nghiã hằng long sl có giá trị là 8865056

Hằng int hệ 8 được viết theo cách 0c1c2c3 ở đây ci là một số nguyên dương trong khoảng từ 1 đến 7 Hằng int hệ 8 luôn luôn nhận giá trị dương

#define h8 0345 Định nghiã hằng int hệ 8 có giá trị là

Trong hệ này ta sử dụng 16 ký tự : 0,1 ,9,A,B,C,D,E,F

Cách viết Giá trị a hoặc A 10 b hoặc B 11 c hoặc C 12 d hoặc D 13 e hoặc E 14 f hoặc F 15

Hằng số hệ 16 có dạng 0xc1c2c3 hặc 0Xc1c2c3 ở đây ci là một số trong hệ 16

Cho ta các hắng số h16 trong hệ 16 có giá trị như nhau Giá trị của chúng trong hệ

Hằng ký tự là một ký tự riêng biệt được viết trong hai dấu nháy đơn, ví dụ 'a'

Giá trị của ký tự 'a' là 97, tương ứng với mã ASCII của nó Hằng ký tự này có thể được sử dụng trong các phép toán giống như các số nguyên khác.

#define kt 'a' Định nghiã hằng ký tự kt có giá trị là 97

Hằng ký tự còn có thể được viết theo cách sau :

' \c1c2c3' trong đó c1c2c3 là một số hệ 8 mà giá trị của nó bằng mã ASCII của ký tự cần biểu diễn

Chữ 'a' có mã hệ 10 là 97, khi chuyển đổi sang hệ 8 sẽ là 0141, do đó hằng ký tự 'a' có thể được viết dưới dạng '\141' Đối với một số hằng ký tự đặc biệt, chúng ta cần thêm dấu '\' để biểu thị.

Cần phân biệt giữa hằng ký tự '0' và '\0' Hằng '0' đại diện cho chữ số 0 với mã ASCII là 48, trong khi hằng '\0' tương ứng với ký tự null có mã ASCII là 0.

Hằng ký tự thực sự là một số nguyên, vì vậy có thể dùng các số nguyên hệ

10 để biểu diễn các ký tự, ví dụ lệnh printf("%c%c",65,66) sẽ in ra AB

Hằng xâu ký tự là một dãy ký tự bất kỳ đặt trong hai dấu nháy kép

#define xau2 "My name is Giang"

Các khái niệm cơ bản về giải thuật

1.2.1 Khái niệm về giải thuật và cấu trúc dữ liệu

Khi giải quyết bài toán trên máy tính điện tử, nhiều người chỉ tập trung vào giải thuật (algorithms), đó là dãy câu lệnh rõ ràng xác định trình tự thao tác trên các đối tượng Mục tiêu là thực hiện một số bước hữu hạn để đạt được kết quả mong muốn.

Giải thuật chỉ phản ánh các phép xử lý, trong khi dữ liệu là đối tượng chính để xử lý trên máy tính điện tử, đại diện cho thông tin cần thiết cho bài toán, bao gồm các dữ kiện đầu vào và kết quả trung gian Khi bàn về giải thuật, không thể không xem xét dữ liệu mà nó tác động, và ngược lại, khi xem xét dữ liệu, cần hiểu rõ giải thuật nào sẽ được áp dụng để đạt được kết quả mong muốn.

Bản thân các phần tử của dữ liệu thường có mối quan hệ với nhau, ngoài ra nếu lại biết

Việc tổ chức dữ liệu theo cấu trúc thích hợp giúp việc thực hiện các phép xử lý trở nên thuận lợi và hiệu quả hơn Mỗi cấu trúc dữ liệu sẽ đi kèm với một thuật toán xử lý tương ứng, và khi cấu trúc dữ liệu thay đổi, thuật toán cũng sẽ thay đổi theo Ví dụ, nếu chúng ta có một danh sách các cặp “Tên đơn vị, số điện thoại” dưới dạng (a1, b1), (a2, b2), …, (an, bn), thì việc lựa chọn cấu trúc dữ liệu sẽ ảnh hưởng trực tiếp đến cách thức xử lý thông tin.

Tôi muốn phát triển một ứng dụng máy tính có khả năng tra cứu số điện thoại dựa trên tên đơn vị Bài toán này chủ yếu liên quan đến phép xử lý cơ bản là tìm kiếm thông tin.

Để tìm số điện thoại tương ứng với tên trong danh sách, chúng ta có thể lần lượt kiểm tra từng tên a1, a2, cho đến khi tìm thấy tên a_i đã chỉ định, sau đó đối chiếu với số điện thoại b_i Tuy nhiên, phương pháp này chỉ hiệu quả khi danh sách điện thoại ngắn, tức là với n nhỏ; còn với n lớn, quá trình này sẽ tốn nhiều thời gian.

Nếu danh mục điện thoại được sắp xếp theo thứ tự từ điển, sẽ có thể áp dụng một thuật toán tìm kiếm hiệu quả hơn, tương tự như cách chúng ta tra cứu trong từ điển.

Việc tổ chức một bảng mục lục chỉ dẫn theo chữ cái đầu tiên của "tên đơn vị" sẽ giúp người dùng dễ dàng tìm kiếm thông tin Cụ thể, khi muốn tìm số điện thoại của Đại Học Bách Khoa, người tìm sẽ không bị mất thời gian xem qua các tên đơn vị có chữ đầu không phải là chữ Đ.

Cấu trúc dữ liệu và giải thuật có mối quan hệ chặt chẽ, tương tự như hình với bóng; không thể tách rời hai khái niệm này khi thảo luận về lập trình và phát triển phần mềm.

Nghiên cứu các cấu trúc dữ liệu là cần thiết để phát triển các giải thuật xử lý hiệu quả trên các cấu trúc đó.

1.2.2 Cấu trúc dữ liệu và các vấn đề liên quan a) Dữ liệu nguyên tử

Trong một bài toán, dữ liệu bao gồm một tập hợp các phần tử cơ sở, được gọi là dữ liệu nguyên tử (atoms) Dữ liệu này có thể là một chữ số, một ký tự, một con số hoặc một từ, tùy thuộc vào từng bài toán cụ thể.

Dựa trên dữ liệu nguyên tử, các cách thức kết nối chúng sẽ tạo ra những cấu trúc dữ liệu đa dạng.

Việc chọn lựa cấu trúc dữ liệu phù hợp để tổ chức thông tin là rất quan trọng, từ đó phát triển thuật toán hiệu quả giúp đạt được kết quả mong muốn cho bài toán.

Trong những năm gần đây, khái niệm về cấu trúc dữ liệu đã phát triển mạnh mẽ, đặc biệt khi ứng dụng máy tính mở rộng ra ngoài các bài toán khoa học kỹ thuật Ban đầu, chỉ có các cấu trúc dữ liệu đơn giản như biến, vector và ma trận, nhưng khi đối mặt với khối lượng dữ liệu lớn, đa dạng và biến động trong các bài toán phi số, những cấu trúc này không còn đủ để thể hiện mối quan hệ mới của dữ liệu Do đó, việc nghiên cứu các cấu trúc dữ liệu phức tạp hơn trở nên cần thiết Đồng thời, các phép toán mới như tạo lập, hủy bỏ, truy cập, bổ sung và loại bỏ phần tử cũng xuất hiện, tạo thành mối liên hệ chặt chẽ giữa cấu trúc dữ liệu và giải thuật.

Các phép này sẽ mang lại những tác dụng khác nhau cho từng cấu trúc Một phép có thể rất hiệu quả với một cấu trúc nhất định nhưng lại không phát huy tác dụng tương tự trên cấu trúc khác.

Khi chọn một cấu trúc dữ liệu, cần xem xét các phép toán liên quan đến cấu trúc đó Ngược lại, khi đề cập đến phép toán, cũng cần chú ý đến cấu trúc mà phép toán đó tác động lên Do đó, khái niệm cấu trúc dữ liệu thường bao hàm cả các phép toán tương ứng Mặc dù trong giáo trình này hai khái niệm được tách biệt, nhưng cấu trúc dữ liệu và các phép toán liên quan vẫn luôn được trình bày song song.

Phân tích và thiết kế giải thuật

1.3.1 Từ bài toán đến chương trình a) Mô – đun hóa và việc giải quyết bài toán

Các bài toán trên máy tính điện tử đang trở nên đa dạng và phức tạp hơn bao giờ hết Các thuật toán và chương trình để giải quyết những bài toán này cũng ngày càng lớn và khó khăn hơn trong việc thiết lập cũng như tìm hiểu.

Để giải quyết một bài toán lớn một cách hiệu quả, chúng ta nên phân chia nó thành các bài toán nhỏ hơn Điều này có nghĩa là xem bài toán chính như một mô-đun, sau đó chia nó thành các mô-đun con Mỗi mô-đun con tiếp tục được phân chia cho đến khi đạt được những phần việc cơ bản mà chúng ta đã biết cách giải quyết Nhờ đó, cấu trúc giải quyết bài toán sẽ được tổ chức theo dạng phân cấp rõ ràng.

Chiến thuật “chia để trị” là phương pháp giải quyết vấn đề hiệu quả, bắt đầu bằng cách thiết kế “từ đỉnh xuống” Phương pháp này phân tích tổng quát toàn bộ vấn đề dựa trên dữ kiện và mục tiêu đã đặt ra, từ đó xác định các công việc chủ yếu Sau khi đã có cái nhìn tổng quan, quá trình sẽ dần đi vào giải quyết các phần cụ thể một cách chi tiết hơn, thể hiện rõ ràng cách thiết kế từ khái quát đến chi tiết.

Ví dụ ta nhận được từ Chủ tịch Hội đồng xét cấp học bổng của trường một yêu cầu là:

Sử dụng máy tính điện tử để quản lý và bảo trì hồ sơ học bổng của sinh viên được tài trợ, đồng thời định kỳ lập báo cáo tổng kết gửi Bộ.

Như vậy trước hết ta phải hình dung được cụ thể hơn đầu vào và đầu ra của bài toán

Chúng ta đã xây dựng một tệp hồ sơ chứa các bản ghi thông tin về học bổng sinh viên, bao gồm số hiệu sinh viên, điểm trung bình theo học kỳ, điểm đạo đức và khoản tiền tài trợ Chương trình cần thiết phải hỗ trợ người dùng trong việc đáp ứng các yêu cầu liên quan đến thông tin này.

1) Tìm lại và hiển thị được bản ghi của bất kỳ sinh viên nào tại thiết bị cuối của người dùng

2) Cập nhật (update) được bản ghi của một sinh viên cho trước bằng cách thay đổi điểm trung bình, điểm đạo đức, khoản tiền tài trợ, nếu cần

3) In bản tổng kết chứa những thông tin hiện thời (đã được cập nhật mỗi khi có sự thay đổi) gồm số hiệu, điểm trung bình, điểm đạo đức, khoản tiền tài trợ

Xuất phát từ những nhận định trên, giải thuật xử lý sẽ phải giải quyết ba nhiệm vụ chính sau:

1) Những thông tin về sinh viên được học bổng, lưu trữ trên đĩa phải được đọc vào bộ nhớ trong để có thể xử lý (nhiệm vụ: “đọc tệp”)

2) Xử lý các thông tin này để tạo ra kết quả mong muốn (nhiệm vụ: “xử lý tệp”)

3) Sao chép những thông tin đã được cập nhật và tệp trên đĩa để lưu trữ cho việc xử lý sau này (nhiệm vụ: “ghi tệp”)

Các nhiệm vụ ở mức đầu thường phức tạp và cần được chia thành các nhiệm vụ con Ví dụ, nhiệm vụ "xử lý tệp" có thể được phân thành ba phần, tương ứng với ba yêu cầu chính đã được đề cập.

- Tìm lại bản ghi của một sinh viên cho trước

- Cập nhật thông tin trong bản ghi sinh viên

- In bảng tổng kết những thông tin về các sinh viên được học bổng

Những nhiệm vụ con này cũng có thể chia thành nhiệm vụ nhỏ hơn Có thể hình dung theo sơ đồ cấu trúc sau:

Thiết kế giải thuật theo kiểu top-down giúp định hướng rõ ràng trong việc giải quyết bài toán, ngăn chặn việc sa đà vào các chi tiết không cần thiết Phương pháp này còn là nền tảng cho lập trình có cấu trúc.

Đối với các bài toán lớn, việc giải quyết thường cần sự hợp tác của nhiều người Phương pháp mô-đun hóa cho phép tách bài toán thành các phần độc lập, giúp các nhóm làm việc hiệu quả mà không ảnh hưởng đến nhau Chương trình được xây dựng dựa trên các giải thuật mô-đun hóa sẽ dễ dàng cho việc tìm hiểu, sửa chữa và chỉnh lý.

Việc chia nhỏ bài toán thành các bài toán con không hề đơn giản và thường đòi hỏi nhiều thời gian cùng công sức Thực tế, nhiệm vụ phân tích và thiết kế giải thuật cho bài toán có thể tốn kém hơn cả việc lập trình Do đó, phương pháp tinh chỉnh từng bước là cần thiết để tối ưu hóa quá trình giải quyết vấn đề.

Tinh chỉnh từng bước là một phương pháp thiết kế giải thuật quan trọng trong lập trình, thể hiện tinh thần của quá trình mô-đun hóa bài toán và thiết kế theo hướng top-down.

Chương trình bắt đầu với việc trình bày giải thuật bằng ngôn ngữ tự nhiên, phản ánh ý chính của công việc cần thực hiện Qua từng bước, các ý tưởng sẽ được chi tiết hóa thành những công việc nhỏ hơn, quá trình này được gọi là tinh chỉnh Tinh chỉnh này sẽ dần dần hướng về ngôn ngữ lập trình đã chọn Ở các bước sau, các mô tả công việc sẽ được thay thế bởi các câu lệnh thường dùng, kết hợp giữa ngôn ngữ tự nhiên và ngôn ngữ lập trình, được gọi là giả ngôn ngữ hay giả mã Như vậy, quá trình thiết kế giải thuật và phát triển chương trình diễn ra từ ngôn ngữ tự nhiên, qua giả ngôn ngữ, đến ngôn ngữ lập trình, chuyển từ mức “làm cái gì” sang “làm thế nào”, ngày càng phù hợp với các chức năng của ngôn ngữ lập trình đã chọn.

Trong quá trình này dữ liệu cũng được “tinh chế” dần dần từ dạng cấu trúc đến dạng lưu trữ cài đặt cụ thể

Ví dụ: Lập một chương trình sắp xếp một dãy n số nguyên khác nhau theo thứ tự tăng dần

Có thể phác thảo giải thuật như sau:

Từ dãy các số nguyên chưa được sắp xếp chọn ra số nhỏ nhất, đặt nó vào cuối dãy đã được sắp xếp

Cứ lặp lại quy trình đó cho tới khi dãy chưa được sắp xếp trở thành rỗng

Ta thấy phác họa trên còn đang rất thô, nó chỉ thể hiện những ý cơ bản

Dãy số ban đầu chưa được sắp xếp, trong khi dãy số đã sắp xếp vẫn còn rỗng Để tiến hành sắp xếp, ta cần chọn số nhỏ nhất đầu tiên và đặt nó vào cuối dãy đã sắp xếp, thực chất là đưa nó vào vị trí đầu tiên của dãy này Tuy nhiên, câu hỏi đặt ra là dãy số này sẽ được đặt ở đâu?

Để hiểu cách sắp xếp dãy số, cần xem xét xem dãy số đó được đặt ở vị trí cũ hay vị trí mới Điều này yêu cầu một cái nhìn sâu sắc hơn về cấu trúc dữ liệu và cách lưu trữ của dãy số đã cho.

Dãy số được coi là các phần tử của một vecto, có cấu trúc giống như mảng một chiều Dãy này được lưu trữ trong một vecto gồm n từ kế tiếp trong bộ nhớ, với mỗi từ a_i lưu trữ phần tử thứ i.

Ta cũng quy ước: dãy số được sắp xếp rồi vẫn để tại chỗ cũ như đã cho

Các thành phần cơ bản và cấu trúc điều khiển chương trình

Các lệnh vào ra dữ liệu

2.1.1 Các hàm vào ra chuẩn a) Hàm getchar() :

Cơ chế vào đơn giản nhất là sử dụng hàm getchar() để đọc từng ký tự từ thiết bị đầu vào, chủ yếu là bàn phím và màn hình của người sử dụng.

Dùng câu lệnh sau : biến = getchar();

Nhận một ký tự vào từ bàn phím và không đưa ra màn hình Hàm sẽ trả về ký tự nhận được và lưu vào biến

Ví dụ : int c; c = getchar(); b) Hàm putchar () : Để đưa một ký tự ra thiết bị ra chuẩn, nói chung là màn hình, ta sử dụng hàm putchar()

Dùng câu lệnh sau : putchar(ch);

Công dụng : Đưa ký tự ch lên màn hình tại vị trí hiện tại của con trỏ Ký tự sẽ được hiển thị với màu trắng

Ví dụ : int c; c = getchar(); putchar(c); c) Hàm getch() :

Hàm nhận một ký tự từ bộ đệm bàn phím, không cho hiện lên màn hình

Dùng câu lệnh sau : getch();

Nếu có sẵn ký tự trong bộ đệm bàn phím thì hàm sẽ nhận một ký tự trong đó

Khi bộ đệm rỗng, máy sẽ tạm dừng cho đến khi có ký tự được gõ Ngay khi người dùng gõ một ký tự, hàm sẽ nhận ký tự đó mà không cần nhấn phím Enter như trong các hàm nhập khác Lưu ý rằng ký tự vừa gõ sẽ không hiển thị trên màn hình.

Thì biến sẽ chứa ký tự đọc vào

Ví dụ : c = getch(); d) Hàm putch() :

Dùng câu lệnh sau : putch(ch);

Hàm này có công dụng hiển thị ký tự 'ch' tại vị trí hiện tại của con trỏ Ký tự sẽ được thể hiện với màu sắc được xác định trong hàm textcolor.

Hàm cũng trả về ký tự được hiển thị

2.1.2 Đưa kết quả lên màn hình

Cách dùng : prinf(điều khiển, đối số 1, đối số 2, );

Hàm printf là một công cụ quan trọng trong lập trình, cho phép chuyển đổi, tạo khuôn dạng và in các đối tượng ra thiết bị đầu ra theo sự điều khiển của xâu điều khiển Xâu điều khiển bao gồm hai loại đối tượng: ký tự thông thường, được in trực tiếp ra thiết bị, và các đặc tả chuyển dạng, mỗi đặc tả này sẽ thực hiện việc định dạng và in các đối tượng tiếp theo của hàm printf.

Chuỗi điều khiển có thể có các ký tự điều khiển :

Dạng tổng quát của đặc tả :

%[-][fw][.pp]ký tự chuyển dạng

Mỗi đặc tả chuyển dạng được bắt đầu bằng ký tự % và kết thúc bằng một ký tự chuyển dạng Giữa ký tự % và ký tự chuyển dạng có thể có dấu ":" để bổ sung thông tin.

Khi không có dấu trừ, kết quả sẽ được dồn về bên phải nếu độ dài thực tế nhỏ hơn độ rộng tối thiểu fw Các vị trí dư thừa sẽ được lấp đầy bằng khoảng trống Đối với các trường số, nếu dãy số fw bắt đầu bằng số 0, các vị trí dư thừa bên trái sẽ được lấp đầy bằng các số.

Khi có dấu trừ, kết quả sẽ được dồn về phía bên trái, trong khi các vị trí dư thừa ở bên phải (nếu có) sẽ được lấp đầy bằng các khoảng trống.

Khi kích thước fw lớn hơn độ dài thực tế của kết quả, các vị trí dư thừa sẽ được lấp đầy bằng khoảng trống hoặc số 0, dẫn đến việc nội dung kết quả sẽ được dịch chuyển sang bên phải hoặc bên trái.

Khi không có firmware hoặc firmware nhỏ hơn hoặc bằng độ dài thực tế của kết quả, độ rộng trên thiết bị sẽ tương ứng với độ dài của kết quả đó.

Tại vị trí của fw ta có thể đặt dấu *, khi đó fw được xác định bởi giá trị nguyên của đối tương ứng

Kết quả ra fw Dấu - Kết quả đưa ra

Tham số pp chỉ được sử dụng khi đối tương ứng là một xâu ký tự hoặc một giá trị kiểu float hay double

Khi đối tượng có kiểu dữ liệu float hoặc double, pp đại diện cho độ chính xác của trường dữ liệu Cụ thể, giá trị được in ra sẽ có pp chữ số sau dấu thập phân.

Khi vắng mặt pp thì độ chính xác sẽ được xem là 6

Khi đối là xâu ký tự :

Nếu độ dài của phần in (pp) nhỏ hơn độ dài của xâu, chỉ ký tự đầu tiên của xâu sẽ được hiển thị Ngược lại, nếu không có phần in hoặc nếu phần in lớn hơn hoặc bằng độ dài của xâu, toàn bộ xâu ký tự sẽ được in ra.

Kết quả ra fw pp Dấu - Kết quả đưa ra Độ dài trường ra

"alphabeta vắng vắng vắng alphabeta 9

Các ký tự chuyển dạng và ý nghĩa của nó :

Ký tự chuyển dạng là một hoặc một dãy ký hiệu xác định quy tắc chuyển đổi và dạng in ra của đối tượng Điều này dẫn đến việc cùng một số có thể được in ra theo nhiều dạng khác nhau Việc sử dụng các ký tự chuyển dạng cần tuân thủ đúng các quy tắc đã được định sẵn Bảng dưới đây cung cấp thông tin chi tiết về các ký tự chuyển dạng.

Ký tự chuyển dạng ý nghĩa

D Đối được chuyển sang số nguyên hệ thập phân

O Đối được chuyển sang hệ tám không dấu ( không có số

X Đối được chuyển sang hệ mưới sáu không dấu ( không có 0x đứng trước )

Đối được chuyển sang hệ thập phân không dấu và được coi là một ký tự riêng biệt Đối là xâu ký tự, các ký tự trong xâu được in cho tới khi gặp ký tự không hợp lệ hoặc đủ số lượng ký tự theo đặc tả độ chính xác pp Đối được xem là float hoặc double và chuyển sang dạng thập phân có dạng [-]m.n nE[+ hoặc -] với độ dài xâu chứa n là pp Ngoài ra, đối cũng có thể chuyển sang dạng thập phân [-]m m.n n với độ dài xâu chứa n là pp, với độ chính xác mặc định là 6 Lưu ý rằng độ chính xác không xác định số chữ số có nghĩa phải in theo khuôn dạng f Cuối cùng, sử dụng %e hoặc %f tùy theo loại nào ngắn hơn và không in các số 0 vô nghĩa.

Tất cả các dãy ký tự không bắt đầu bằng dấu % hoặc không kết thúc bằng ký tự chuyển dạng đều được coi là ký tự hiển thị Để hiển thị các ký tự đặc biệt, cần tuân thủ quy định này.

Lệnh này tương đương với printf("\n%f\n%8.2f",x,n,y);

Vì n=8 tương ứng với vị trí *

2.1.3 Vào dữ liệu từ bàn phím

Hàm scanf là một công cụ quan trọng trong việc đọc dữ liệu từ bàn phím, chuyển đổi thông tin thành các kiểu dữ liệu như số nguyên, số thực và ký tự, sau đó lưu trữ chúng vào bộ nhớ tại các địa chỉ xác định.

Cách dùng : scanf(điều khiển,đối 1, đối 2, );

Xâu điều khiển chứa các đặc tả chuyển dạng, mỗi đặc tả sẽ tạo ra việc đổi dạng biến tiếp sau của scanf

Biểu thức

Biểu thức toán học là sự kết hợp giữa các phép toán và toán hạng, thể hiện một công thức cụ thể Mỗi biểu thức đều có giá trị riêng, và các thành phần như hằng, biến, phần tử mảng, và hàm cũng được coi là biểu thức.

Trong C, ta có hai khái niệm về biểu thức :

Biểu thức được phân loại thành hai kiểu giá trị chính: nguyên và thực Trong logic, các biểu thức được phân chia thành hai loại: đúng, tương ứng với giá trị khác 0, và sai, với giá trị bằng 0.

Biểu thức thường được dùng trong :

Vế phải của câu lệnh gán

Làm tham số thực sự của hàm

Trong các toán tử của các cấu trúc điều khiển

Trong bài viết này, chúng ta sẽ khám phá hai khái niệm chính tạo nên biểu thức: toán hạng và phép toán Toán hạng bao gồm các thành phần như hằng, biến, phần tử mảng và hàm mà chúng ta đã thảo luận trước đó Các phép toán sẽ được trình bày chi tiết hơn trong chương 6.

2.2.2 Lệnh gán và biểu thức:

Biểu thức gán là biểu thức có dạng : v=e

Biểu thức gán là một cấu trúc cơ bản trong lập trình, bao gồm một biến (hay phần tử mảng) và một biểu thức Khi thực hiện biểu thức gán, giá trị của biểu thức sẽ được gán cho biến, và kiểu của biểu thức sẽ được xác định bởi kiểu của biến Để tạo thành phép toán gán, biểu thức gán thường được kết thúc bằng dấu chấm phẩy (;), tạo thành cấu trúc v=e;.

Biểu thức gán có thể được áp dụng trong các phép toán và câu lệnh tương tự như các biểu thức khác Chẳng hạn, khi viết a=b=5, điều này có nghĩa là giá trị của b được gán là 5, và đồng thời biến a cũng nhận giá trị 5 Kết quả cuối cùng là b=5 và a=5.

Hoàn toàn tương tự như : a=b=c=d=6; gán 6 cho cả a, b, c và d

Ví dụ : z=(y=2)*(x=6); { ở đây * là phép toán nhân } gán 2 cho y, 6 cho x và nhân hai biểu thức lại cho ta z

2.2.3.1 Các phép toán số học :

Các phép toán hai ngôi số học là

Phép toán ý nghiã Ví dụ

( Chia số nguyên sẽ chặt phần thập phân )

( Cho phần dư của phép chia a cho b )

Có phép toán một ngôi - ví du -(a+b) sẽ đảo giá trị của phép cộng (a+b)

Các phép toán cộng (+) và trừ (-) có cùng thứ tự ưu tiên, nhưng thứ tự ưu tiên của chúng thấp hơn so với các phép toán nhân (*), chia (/), và lấy phần dư (%) Đồng thời, cả ba phép toán này lại có thứ tự ưu tiên thấp hơn so với phép trừ một ngôi.

Các phép toán số học được thực hiện theo thứ tự từ trái sang phải, với số ưu tiên và khả năng kết hợp của các phép toán sẽ được trình bày trong phần sau.

2.2.3.2 Các phép toán quan hệ và logic :

Phép toán quan hệ và logic cung cấp hai giá trị cơ bản: đúng (1) và sai (0) Khi các điều kiện được đặt ra là đúng, giá trị trả về sẽ là 1; ngược lại, nếu điều kiện sai, giá trị sẽ là 0.

Các phép toán quan hệ là :

Phép toán ý nghiã Ví dụ

>= So sánh lớn hơn hoặc bằng

b 4>5 có giá trị 0 a>=b 6>=2 có giá trị 1 a0 ) /* if thứ nhất*/

Khi muốn thực hiện một trong n quyết định ta có thể sử dụng cấu trúc sau : if ( biểu thức 1 ) khối lệnh 1; else if ( biểu thức 2 ) khối lệnh 2;

else if ( biểu thức n-1 ) khối lệnh n-1; else khối lệnh n;

Trong cấu trúc này, máy sẽ đi kiểm tra từ biểu thức 1 trở đi đến khi gặp biểu thức nào có giá trị khác 0

Nếu biểu thức thứ i (1,2, n-1) có giá trị khác 0, máy sẽ thực hiện khối lệnh i, rồi sau đó đi thực hiện lệnh nằm tiếp theo khối lệnh n trong chương trình

Nếu trong n-1 biểu thức không có biểu thức nào khác bằng 0, máy sẽ thực hiện khối lệnh n và sau đó tiếp tục với lệnh tiếp theo trong chương trình.

Chương trình giải phương trình bậc hai

{ float a,b,c,d,x1,x2; printf("\n Nhap a, b, c:"); scanf("%f%f%f,&a&b&c); d=b*b-4*a*c; if (d

Ngày đăng: 23/12/2023, 10:14

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN