【CS 61b study notes 2】Linked List & Array

Basic data info

Declaring a Variable

  • Variable Definition

    • className variableName = new className ();
      • for reference type
      • e.g. : IntList L1 = new IntList(5, null);
    • primitiveType variableName = value;
      • primitive types are built-in data type in java and can be used directly without using any “new” keyword.
      • e.g. : int x = 10;
    • Some rules
      • A variable name can consist of Capital letters A-Z , lowercase letters a-z , digits 0-9 and two special character such as _underscore and $ dollar sign.
      • First character can not be digit
      • Can NOT use Blank spaces and java keywords
      • No limit in the length ,but by convention 4 to 15 chars
      • Always exist on the left-hand side of assignment operators =
      • Some vaild example : variableName , variablename , _variableName ,$variableName , variableName1, variableName_1, VARIABLENAME
  • Example

    String str1 = "abc";
    String str2 = "abc";
    
    String str3 = new String("abc");
    String str4 = new String("abc");
    String str5 = str3;
    
    System.out.println(str1 == str2); // true
    System.out.println(str3 == str4); // false
    System.out.println(str1 == str3); // false
    System.out.println(str3 == str5); // true
    System.out.println(str3.equals(str4)); // true
    System.out.println(str3.equals(str1)); // true
    System.out.println(str3.equals(str5)); // true
    System.out.println(str1.equals(str2)); // true
    
    
  • For the reference type ,“==” checks the address (or the object), while .equal() checks the value(or the content).

  • When we assign “abc” to str1 , java will not build a new string object immediately .Instead, java checks whether there exists “abc” in the stack constant field first. If thers is no “abc” , it will build a constant “abc” in the stack.

  • The assignment of str2 is after the str1 , so ‘abc’ has already existed in the constant field. str2 will point at the

  • the new keyword creates a new String object and returns the address of the overall object for assignment to str3 . Although the values of str1 ,str3 and str4 are both “abc” , the addresses are different.

  • Every time we use new keyword, java will create a completely different object and assign a new address to it , which means the address that java assigns to the new object is independent of what the object contains.

When we declare a variable of a certain type, java finds a contiguous block with exactly enought bits to hold a thing of that type. Each data type in java holds a different number of bits. Unlike C or python ,java can not access the exact address of the data . In java , the exact memory address is below the level of the abstraction accessible to us.

  • Local Variable
    • Local Variables can be declared in methods, code blocks, constructors, etc in java.
    • Local variables do not have any default values in java
    • Local variable can be used only after it has been assigned
    • declaring a variable without initialization : int num
    • Initializing the variable : int num = 100;
// error : variable might not have been initialized
public class test{
	public static void main(String[] args){
		// local variable must be initialized before using ,otherwise complier errors
		int a ;
		System.out.println(a);
	}
}
  • Declare a static global primitive type variable without initialization
// output : 0
public class test{
	// static declaration for global int variable is important owing to  the static main function
	static int a ;
	public static void main(String[] args){
		System.out.println(a);
	}
}
  • Primitive Type : 8
    • byte, short, long, int, float, double, boolean, char
  • Inaddition to above 8 primitive types, others are the reference types. That is , everything else, including arrays , is not primitive type but rather a reference type.
  • When we instantiate an Object using new, java first allocates a box for each instance variable of the class , and fills them with a default value.
Primitive Data TypeSizeDefault Values
byte1 byte0
short2 bytes0
int4 bytes0
long8 bytes0
float4 bytes0.0
double8 bytes0.0
boolean1 bitfalse
char1 byte\u0000’ or null
  • Reference Variable Declaration
    When we declare a variable of any reference type (Walrus, array etc.) , java allocates a box of 64 bits, no matter what type of object. The 64 bit box contains not the data itself , but instead the address of the data memory.
// example
Walrus someWalrus; // create a box of 64 bits
someWalrus = new Walrus(1000, 8.3);

The first line creates a box of 64 bits. The second line creates a new Walrus , and the address is returned by the new operator. These bits are then copied into the someWalrus box according to the GRoE.

  • Box and Pointer Notation

    • If an address is all zeros, we will represent it with null.
    • A non-zero address will be represented by an arrow pointing at an object instantiation.
  • Parameter passing
    When we pass parameters to a function , we are also simply copying the bits. Copying the bits is usually called “pass by value”. In java , we always pass by value. When a function is invoked , the function has its own scope with corresponding quantity new boxes , and the bits are simply copied in . If we just change the varables in the function, the variables that was just passed in won’t be changed.

  • Instantiation of Arrays
    Instantiating an array is very similar to instantiating an object. If we create an integer array of size 5 : x = new int[] {1, 3, 4, 5, 8} . Then the new keyword creates 5 boxes of 32 bits each and returns the address of the overall object for assignment to x. Objects can be lost if you lose the bits corresponding to the address . For example if the only copy of the address of a particular Walrus is stored in x , then x = null will cause you to permanently lose the Walrus.

List

IntList

It is similar with the Linked List in cs61a. It is a naked recursive data structure and is hard to read and maintain.

public class IntList{
	public int first;
	public IntList rest;
	 
	// constructor
	public IntList(int f, IntList r){
		first = f;
		rest = r;
	}
	/** return the size of the list using recursion*/
	public int size(){
		if(rest == null){
			return 1;
		}
		return 1 + this.rest.size();
	}
}

iterativeSize : In this method , we first assign this to the pointer p . It is necessary because we cannot reassign this in Java.

/* return the size of the list using no recursion*/
public int iterativeSize(){
	IntList p = this;
	int totalSize = 0;
	while (p!=null){
		totalSize += 1;
		p = p.rest;
	}
	return totalSize;
}

SLList

Some improvements :

  • Using nested class
    • A class is really just a supporting character for the B class.
      • In this case , IntNode just plays a supportive roles in the story of SLList.
    • Declaring a nested class as static means that methods inside the static class CAN NOT access(use) any of the members of the enclosing class(outer class).
      • In this case ,IntNode has no need to use any of the instance methods or variables of SLList .So we declare IntNode as static,which means that no method in IntNode would be access to first, addFirst or getFirst.
      • Saving a bit of memory . Because each IntNode no longer needs to keep track of how to access its enclosing class , that is, IntNode class does not get a reference to the SLList.
  • Using private keyword
    • Private variables and methods can only be accessed by code inside the same .java file.
    • Likewise , the public keyword should be thought of as a declaration that a method is available and will work forever exactly as it does now.
public class SLList{
	// improvement : using nested class and declaring it as static
	public static class IntNode{
		public int item;
		public IntNode next;
		public IntNode(int i, IntNode n){
			item = i;
			next = n;
		}
	}
	
	// improvement : using private 
	private IntNode fitst;
	// improvement : caching 
	private int size;
	
	// constructor
	public SLList(int x){
		first = new IntNode(x, null);
		size = 1;
	}
	
	/** add an intem to the front of the list */
	public void addFirst(int x){
		first = new IntNode(x ,first);
		size += 1;
	}
	
	/** retrieves the front item from the list */
	public int getFirst(){
		return first.item;
	}
	
	/** Adds an item to the end of the list*/
	
	public void addLast(int x){
		IntNode p = first;
		
		while (p.next != null){
			p = p.next;
		}
		
		p.next = new IntNode(x, null);
	}
	
	/** Return thr size of the list starting at IntNode p*/
	private static int size(IntNode p){
		if (p.next == null){
			return 1;
		}
		return 1+ size(p.next);
	}
	
	// It is allowed that two different methods have the same names with different parameters. overloading --
	
	
	/** return the number of items in the list using recursion */
	public int size(){
		return size(first);
	}
}

  • Sentinel Nodes
    • Holding a value which we won’t care about.
 /** An SLList is a list of integers, which hides the terrible truth
   * of the nakedness within. */
public class SLList {	
	private static class IntNode {
		public int item;
		public IntNode next;

		public IntNode(int i, IntNode n) {
			item = i;
			next = n;
			System.out.println(size);
		}
	} 

	/* The first item (if it exists) is at sentinel.next. */
	private IntNode sentinel;
	private int size;

	private static void lectureQuestion() {
		SLList L = new SLList();
		IntNode n = IntNode(5, null);
	}

	/** Creates an empty SLList. */
	public SLList() {
		sentinel = new IntNode(63, null);
		size = 0;
	}

	public SLList(int x) {
		sentinel = new IntNode(63, null);
		sentinel.next = new IntNode(x, null);
		size = 1;
	}

 	/** Adds x to the front of the list. */
 	public void addFirst(int x) {
 		sentinel.next = new IntNode(x, sentinel.next);
 		size = size + 1;
 	}

 	/** Returns the first item in the list. */
 	public int getFirst() {
 		return sentinel.next.item;
 	}

 	/** Adds x to the end of the list. */
 	public void addLast(int x) {
 		size = size + 1; 		

 		IntNode p = sentinel;

 		/* Advance p to the end of the list. */
 		while (p.next != null) {
 			p = p.next;
 		}

 		p.next = new IntNode(x, null);
 	}
 	
 	/** Returns the size of the list. */
 	public int size() {
 		return size;
 	}

	public static void main(String[] args) {
 		/* Creates a list of one integer, namely 10 */
 		SLList L = new SLList();
 		L.addLast(20);
 		System.out.println(L.size());
 	}
}
  • Invariant
    An invariant is a fact about a data structure that is guaranted to be true .
    A SLList with a sentinel node has at least the following invariants
    • The sentinel reference always points to a sentinel node
    • The front item(if it exists) , is always at sentinel.next.item
    • The size variable is always the total number of items that have been added

DLList

  • Question Description
    SLList drawbacks : addLast() is slow
    A naive solution : Recall that we cached the size of our list as an instance variable of SLList . What if we cached the last element in our list as well?
    All of a sudden, addLast() is fast again ; we access the last element immediately ,then add our element in.
    But removeLast() is still slow.
    In removeLast(), we have to know the second-to-last element is, so we can point our cached last variable to it.
    We could then cache a second-to-last variable, but now if if ever want to remove the second to last element, I need to know where our third-to-last element is . How to solve?

  • Basic solution idea : DLList
    The solurion is to give each IntNode a prev pointer , pointing to the previous item.
    This creates a Doubly-Linked-List.
    With this modification, adding and removing from the front and back of out list becomes fast ( while adding/removing from the middle remains slow)

  • Incorporating the Sentinel

    • Recall that we added a sentinel node to the SLList . For DLList , we can either have two sentinels ( one for the front , one for the back ).
    • Using the circular sentinel :
      • A DLLIist using a circular sentinel has one sentinel.
    • The sentinel point to the first element of the list with next.
    • The sentinel point to the last element of the list with prev.
    • The last element of the list’s next points to the sentinel
      circular-sentinel
      在这里插入图片描述

Genetic type

Here , BleepBlorp is just a name that represents data type, we can use most any other name we might care to use instead.

// a genetic DLList that can hold any type would look as below
public class DLList<BleepBlorp>{
	private IntNode sentinel;
	private int size;
	
	public class IntNode{
		public IntNode prev;
		// public int item;
		public BleepBlorp item;
		public IntNode next;
	}
}

Now that we have defined a genetic version of the DLList class ,we must also use a special syntax to instantiate this class. Now we need to put a desired type inside of angle brackets during declaration, and also use empty angle brackets during instantiation.
Since generics only work with reference types, we cannot put primitives like int or double. Instead , we use the reference version of the primitive type . If we want to represent int in the angle brackets. we can use Integer instead.

DLList<String> d1 = new DLList<>("test");
d1.addLast(" DLList");
  • Some rules of generic types
    • In the .java file implementing a data structure , specify your generic type name only once at the very top of the file after the class name.
    • In other .java files, which use your data structure , specify the specific desired type during declaration, and use the empty diamond operator <> during instantiation.
    • If you need to instantiate a generic over a primitive type, use Integer, Double, Character, Boolean, Long, Short, Byte or Float instead of their primitive equivalents.

The Alist

AList can be used to store arbitrarily long lists of data, similar to our DLList . Unlike the DLList , the AList will use arrays to store data instead of a linked list.

  • AList Invariants

    • The position of the next item to be inserted is always size.
    • size is always the number of items in the AList.
    • The last item in the list is always in position size - 1.
  • Array Resizing

    • When size == array.length or size << array.length , we need to make a new array
      • int[] a = new int[new_size];
      • System.arraycopy(array, 0 , a , 0 , new_size);
      • a[new_size] = TARGET;
      • array = a;
      • size = new_size;
  • removeLast
    Notice that any change to our list must be reflected in a change in one or more memory boxes in the implementation.

// adding the new item 
int[] a = new int[size+1];
System.arraycopy(items, 0, a, 0, size);
a[size] = 11;
items = a ;
size = size + 1;
  • generic AList
public class AList<Glorp>{
	private Glorp[] items;
	private int size;
	
	public Alist(){
		items = (Glorp[]) new Object[8];
		size = 0;
	}
	
	private void resize(int cap){
		Glorp[] a = (Glorp[]) new object[cap];
		System.arraycopy(items, 0, a, 0, size);
		items = a;
	}
	
	public Glorp get(int i){
		return items[i];
	}
	
	public Glorp getback(){
		return items[size-1];
	}
	
	public GLorp deleteBack(){
		Glorp returnItem = getback();
		items[size -1 ] = null;
		size -= 1;
		return returnItem;
	}
}

Unlike the Integer based ALists , we actually want to null out deleted items.
- Java only destroys unwanted objects when the last reference has been lost;
- Keeping reference to unneeded objects is sometimes called loitering
- Save memory , do not loiter.

  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值