30/09/2018, 18:12
Chưa hiểu được bản chất của danh sách liên kết đơn, mong được gải đáp
Khi chạy nó chỉ in ra bệnh nhân đầu tiên. Em nghĩ là XuatMotBN(l.pHeap->info, i); sai nhưng không hiểu tại sao nó lại sai, Và sẽ sửa nó như thế nào. Mọi người cố gáng đọc tí code của em hơi dài. Xin cảm ơn!
#include <stdio.h>
struct BN
{
int maBN;
char hoten[30];
char ngaynhap[20];
float vienphi;
};
struct Node
{
BN info;
Node *Next;
};
struct List
{
Node *pHeap;
Node *pTail;
};
Node* TaoMotNode(BN x)
{
Node *p = new Node;
if(p == NULL)
{
printf("khong du bo nho");
return NULL;
}
p->info = x;
p->Next = NULL;
return p;
}
void NhapMotBN(BN &bn)
{
printf("Nhap ma so benh nhan: ");
scanf("%d", &bn.maBN);
printf("Nhap ho ten benh nhan: "); fflush(stdin);
gets(bn.hoten);
printf("Nhap ngay nhap vien: "); fflush(stdin);
gets(bn.ngaynhap);
printf("Nhap vien phi: ");
scanf("%f", &bn.vienphi);
}
void XuatMotBN(BN bn, int i)
{
printf("%-3d %-5d %-20s %-10s %10.2f
", i, bn.maBN, bn.hoten, bn.ngaynhap, bn.vienphi);
}
void ThemDauDS(List &l, Node *x)
{
if(l.pHeap == NULL) //ds rong
l.pHeap = l.pTail = x;
/*
l.pHeap = x;
l.pTail = l.pHeap;
*/
else
{
x->Next = l.pHeap;
l.pHeap = x;
}
}
void TaoDS(List &l, int &n)
{
BN x;
Node *p = new Node;
for(int i=1; i<=n; i++)
{
NhapMotBN(x);
p= TaoMotNode(x);
ThemDauDS(l,p);
}
}
void Output(List l)
{
Node *p = l.pHeap;
int i=1;
printf("%-3s %-5s %-20s %-10s %10s
", "STT", "MA BN", "HO TEN", "NGAY NHAP", "VIEN PHI");
while(l.pHeap != NULL)
{
XuatMotBN(l.pHeap->info, i); //có vấn đề
i++;
p = p->Next;
}
}
void main()
{
List l;
l.pHeap = l.pTail = NULL; //tao danh sach rong
int n;
printf("Nhap so luong benh nhan: ");
scanf("%d", &n);
TaoDS(l,n);
Output(l);
}
Bài liên quan
vòng lặp có vấn đề mới đúng. Có phải là in ra bệnh nhân đầu tiên liên tục rồi báo lỗi dereference NULL?
XuatMotBN(l.pHeap->info, i);
ở đâyl.pHeap
ko thay đổi (mà chỉ cóp
thay đổi) vậy thì làm sao in ra đúng được.p
trỏ tới bệnh nhân cần in, phải xuấtp
chứ ko phải xuấtl.pHeap
và cuối cùng là điều kiện vòng lặp.
l.pHeap
cố định ko thay đổi thì vòng lặp chạy mãi? Với lại dòngp = p->Next;
nếup->Next
là NULL vậy thì vòng lặp tiếp theo làm sao dereference nó để lấyp->Next
được?thêm mấy lỗi nữa (ko cần sửa vẫn chạy được nhưng ko ổn)
List
TaoDS()
,p
ban đầu trỏ vào 1Node
mà ko xài, cũng ko giải phóng => dù có viết hàm hủy thì cũng tràn bộ nhớ. Sửa lại làNode *p = NULL;
TaoMotNode()
thì truyền BN là truyền bản copy, mất công copy dư 1 bệnh nhân. Thêm 100 bệnh nhân là copy dư 100 bệnh nhân ko xài. Hơn nữa lại copyx
vàop->info
, như vậy là dư 1 lần copy nữa. Để nhập 1 bệnh nhân thì cần tới 3 struct BN (x
trongTaoDS()
, bản copy củax
khi truyền vào hàmTaoMotNode()
, vàp->info
trong hàmTaoMotNode()
). Chỉ cần xài 1 bản là đủ rồi…pHeap
cũng sai chính tả… Heap là đống, head mới là đầu.pHead
mới đúng chính tả…Đã sửa rồi nhưng không hiểu tại sao nó lại in danh sách theo thứ tự từ cuối đi lên đầu bạn @tntxtnt ơi
Không hiểu cho lắm lúc đầu mình cấp phát bộ nhớ cho con trỏ p *Node p = new Node; và sau đó thì gán p= TaoMotNode(x); là đã dùng nó rồi nhỉ
Node *p = new Node; là p đã trỏ tới 1 Node rồi, p= TaoMotNode(x); là p nó trỏ tới Node khác (được cấp phát trong hàm TaoMotNode). Vậy là 1 Node ban đầu bị thừa ko xài.
từ cuối đi lên đầu là đúng rồi, tại bạn thêm Node vào pHead, ví dụ như xếp 1 chồng tập vậy. Thêm tập thì nó cao dần lên (head tăng, tail ko đổi), tập cuối cùng ở trên đầu. Khi lấy tập ra thì lấy từ trên xuống (head -> tail) vậy là lấy tập cuối rồi kế cuối rồi kế kế cuối v.v… tới tập đầu tiên.
muốn nó in đúng thì 1 là khi xuất ra xuất từ tail ngược về head. 2 là khi thêm node thì thêm vào phía sau tail chứ ko phải thêm vào phía trước head.
Ok nhưng còn chỗ này mình chưa rõ cho lắm, Bạn giúp mình sửa lại hàm TaoMotNode() với
à đây là dslk đơn thì ko đọc ngược từ tail về head được, phải thêm từ đuôi thôi.
đơn giản lắm:
hàm TaoDS chuyển thành 2 dòng:
TaoMotNode trả về con trỏ, lấy con trỏ đó cho vào ThemDauDS luôn, khỏi cần tạo biến trung gian.
hoặc nếu ko thích nhập bệnh nhân ở TaoMotNode thì có thể chuyển về TaoDS như ban đầu. Gọi
NhapMotBN(l.pHead->info);
sau khi ThemDauDS là được.nếu muốn in xuôi thì viết hàm ThemDuoiDS (thêm vào đuôi). Chỉ sửa có 2 dòng trong ThemDauDS thôi.
ok thật là thâm thuý Phải nói là cảm ơn @tntxtnt rất nhiều! Nhưng còn chỗ này chưa hiểu lắm Hàm ThemDauDS()
x->Next = l.pHead;
l.pHead = x; //Lúc này pHead nó đã trỏ về đầu danh sách (1)
Hàm Output() thì ta khởi tạo con trỏ p trỏ tới pHead (1) thì khi duyệt danh sách thì nó phải duyệt từ đầu chứ nhỉ
bây giờ thêm 1 2 3 lần lượt vào đầu dslk:
hay đơn giản là 1 rồi 2->1 rồi 3->2->1
thì nó bị thêm ngược rồi. Nếu thêm từ đuôi thì sẽ là 1 rồi 1->2 rồi 1->2->3
truy lần lượt từng dòng code là
Tương tự khi thêm 3 vào. Vậy là thành 3->2->1
ok đã thông não rồi cảm ơn @tntxtnt nhiều !
viết hàm ThemDuoi đi. Có pTail rồi thì thêm cũng dễ mà
viết thêm hàm hủy nữa. Cũng lặp 1 vòng như khi in ra thôi, nhưng phải có thêm 1 con trỏ nữa để giữ địa chỉ Node tiếp theo cần delete, vì nếu delete con trỏ hiện tại thì ko thể lấy p = p->next được nữa.
Hàm thêm đuôi thì đã viết được còn huỷ với sắp xếp trên trường chưa học tới nhưng sẽ nghiên cứu làm tiếp có gì lên hỏi bạn nữa nhé
không biết lỗi gì luôn @tntxtnt vào xem giúp sai chỗ nào thế nhỉ.
mỗi lần gọi TaoMotNode() là nó ra 1 node mới rồi. Chỉ gọi đc TaoMotNode() 1 lần thôi
trong ThemCuoi thì có thể sửa thành
còn ThemDau thì chắc phải có 1 con trỏ trung gian:
ThemCuoi trường hợp
l.pHead != NULL
có thể viết thành 1 dòng:vì toán tử ‘=’ được xét từ phải sang trái, nên
l.cuoi->next = TaoMotNode();
được tính trước rồi trả vềl.cuoi->next
, sau đól.cuoi = l.cuoi->next
được tính tiếp. Thành ra có thể viết gộp lại 1 dòng.ThemDau thì ko gộp được
chỗ này là sao không hiểu phải là Node *newNode = TaoMotNode(); chứ nhỉ
Bạn xem giúp mình có cần chỉnh sửa chổ nào cho nó tối ưu ngon lành luôn
ờ đúng rồi ta ghi nhầm, copy chỉnh sửa
tại vì nếu ghi là
TaoMotNode()->next = l.dau;
thì ko biết Node phía trướcl.dau
hay Node mới tạo ở đâu (ko có con trỏ nào trỏ vào nó). Nên phải có 1 node tạm thời, gọi là newNode ghi địa chỉ nó ở đâu, rồi mới “gắn” vàol
được.còn trong ThemCuoi thì do node mới thêm vào liền sau
l.cuoi
hay cól.cuoi->next
trỏ tới nên biết là nó ở đâu.trong hàm xuất vòng lặp có thể viết gọn lại, xài
for
i++ trả về i rồi mới tăng giá trị của i, viết gộp với XuatMotBN thành 1 dòng.
khai báo
p
, checkp != NULL
, rồi gánp = p->next
3 dòng có thể gộp thành 1 dòngfor
cái này là clean up cho code gọn hơn thôi ko ảnh hưởng gì tới performance cả.
.
.
.
void XuatMotBN(BN bn, int i);
sửa thành
void XuatMotBN(const BN &bn, int i)
nếu ko thì mỗi lần xuất 1 BN lại mất công copy ra 1 bản.
Luôn luôn truyền bản chính (tham chiếu
&
). Nếu ko phải chỉnh sửa gì thì thêmconst
vào. Trừ vài trường hợp mới truyền bản copy: mấy type nhỏ hay primitive types như int, float, double, char, v.v… ở đây struct BN cả trăm bytes nên truyền bản chính.const trong trường hợp này có tác dụng gì nhỉ
const ko cho phép hàm nhận x chỉnh sửa x.
ở đây truyền bản chính thì có nguy cơ hàm nhận bản chính sẽ chỉnh sửa bản chính, còn truyền bản copy thì ko sợ hàm nhận bản copy này sẽ chỉnh sửa bản chính, nhưng lại mất công copy ra bản khác. Vì vậy nên truyền bản chính. Để ko cho phép hàm nhận chỉnh sửa thì thêm const vào là xong.